2017-10-12(木) JUnit5で変わるテストの書き方
■[java] JUnit5で変わるテストの書き方
JUnit5が案外よさげなので、JUnit5を使うとどんな感じでテストが変わるのか考えてみます。
実際にどこが変わったかとか、使い方自体はいろいろまとめられたブログがあるし、公式ドキュメントも読みやすいのでそちらを。
http://junit.org/junit5/docs/current/user-guide/
メソッドごとのテスト
JUnit5でいいのは、Nestedですね。
いままで、いろんなメソッドを対象にしたテストが入り混じってたと思います。
import org.junit.Before; import org.junit.Test; public class PurchaseTest { @Before public void setup() { // 全体のセットアップ // purchase()用のセットアップ // history()用のセットアップ } @Test public void purchase_success() { } @Test public void purchase_insufficient() { } @Test public void purchase_soldout(){ } @Test public void history() { } @Test public void history_nodata() { } }
こんな感じ。hoge_case1みたいなのがたくさん並んで、わかりにくかった。
各メソッド用のセットアップも、全部まとめるか、セットアップ用メソッドを定義して呼び出すか、みたいになっていました。
これをNested使うと、こう。
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; public class PurchaseTest { @BeforeEach void setup() { // 全体のセットアップ } @Nested class purchase { @BeforeEach void setup() { // purchase()用のセットアップ } @Test void success() { } @Test @DisplayName("金額不足") void insufficient() { } @Test void soldout(){ } } @Nested class history { @BeforeEach void setup() { // history()用のセットアップ } @Test void normal() { } @Test void nodata() { } } }
public指定が不要になったのも、地味にありがたい。
メソッドごとのテストをNestedするためのクラス名は、メソッドにあわせて小文字始まりにするのもいいかなと思うけど、IDEとかに警告されそうでもある。
テスト区分
JUnit5からcategoryの代わりにtagが導入されましたが、アノテーションが短くなったところで、いちいちつけるのは面倒。
メタアノテーションが使えるので、これも便利に。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Tag("integration") @Test public @interface MySqlIT { } @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Tag("integration") @Test public @interface SpringIT { }
こんな感じのアノテーションを定義して、
@MySqlIT void store() { // 外部のMySQLにアクセスしちゃうテスト } @SpringIT void login() { // よそのSpringを呼び出したりするテスト }
みたいな感じでテストを書ける。
で、ビルドスクリプトには一括でexcludeできたり。
<configuration> <properties> <excludeTags>integration</excludeTags> </properties> </configuration>
細分化しすぎると使いにくそうだけど。
パラメタライズテスト
ちょっとずつパラメータ変えたテストを書くのも面倒だったけど、だいぶ楽になります。
junit-jupiter-paramsを別にdependencyに付け加える必要があるので注意。
@ParameterizedTest(name="param test {0}") @ValueSource(strings = {"one", "two"}) void paramTest(String param) { Assertions.assertEquals("one", param); }
値の生成には、@ValueSourceのほかにも@CsvSourceとか@MethodSourceとかあるので便利そう。
pom
参考までに、これ動かしたpom。
モジュール構成はこんな感じになってます。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>kis</groupId> <artifactId>junit5sample</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> <junit.jupiter.version>5.0.1</junit.jupiter.version> <junit.platform.version>1.0.1</junit.platform.version> </properties> <build> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.19.1</version> <dependencies> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-surefire-provider</artifactId> <version>${junit.platform.version}</version> </dependency> </dependencies> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-launcher</artifactId> <version>${junit.platform.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-engine</artifactId> <version>${junit.jupiter.version}</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-params</artifactId> <version>${junit.jupiter.version}</version> <scope>test</scope> </dependency> </dependencies> </project>