Edited at

JUnit 5 M1 をやってみよう

More than 3 years have passed since last update.


本資料について



自己紹介


  • 福原和朗 @kazurof


    • Qiita, twitter, Facebook とかやってます。



  • 所属


    • GMOリサーチ


      • アンケートシステムの開発保守







本日のお題


  • JUnit 5.0.0-M1 が、2016/7/7にリリースされました


    • 七夕さま :tanabata_tree:



  • いじってみた感触など並べてみます。

  • 注意:Milestoneなので今後変更入るかもしれません。



そもそもの大前提


  • 元となるパッケージ名が変わった。


    • org.junit.jupiter


    • org.junit.platform など

    • JUnit4 とのAPI互換性はありません。



  • Java8以降をサポート。


    • Java7以前はさようなら。





JUnit5 のサブモジュール


  • JUnit Platform


    • テストの実行の全体枠組み



  • JUnit Jupiter


    • テスト開発者・拡張の開発者向けAPI



  • JUnit Vintage


    • JUnit3, 4 の実行API





Jupiter という名前の由来



JUnit5で何が変わったか?


  • 基本的なやり方は変わっていない。

@Test テストメソッド

@BeforeEach 事前処理

@AfterEach 事後処理



サンプル

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.jupiter.api.Test;

class FirstJUnit5Tests {
@Test
void myFirstTest() {
assertEquals(2, 1 + 1);
}
}



無くなった(?)もの


  • assertThat

アサーション系の機能は好きなもの使ってね、というスタンスらしい。



無くなった(?)もの :two:


  • TestSuite


    • 複数のテストを木構造のようにグループ化

    • テストメソッドに"タグ"をつけるやり方に移行


      • @Tag("nantoka")



    • 従前、Categoriesと呼ばれていた機能。


      • 以前はexperimentalだった。







JUnit4 のTestSuite

//こんなボイラープレートコードは大変かも!

package tryjunit4.suite;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
import org.junit.runners.Suite.SuiteClasses;
@RunWith(Suite.class)
@SuiteClasses( { SampleTest.class, AnotherTest.class,
tryjunit4.subpack.AllTests.class })
public class AllTests {
}



JUnit5 の@Tagの例

import org.junit.jupiter.api.Tag;

import org.junit.jupiter.api.Test;
//"fast"タグを指定してテスト実行などできる
@Tag("fast")
class TaggingDemo {
@Test
@Tag("taxes")
void testingTaxCalculation() {
}
}



無くなった(?)もの :three:



  • @Ruleによる拡張


    • なくなる見込み。多分。

    • そもそも、フィールドを使う形なので大仰になりがちだった。

    • 新たに設計された拡張が導入される。

    • @ExtendWith




import org.junit.rules.TestName;

public class TestNameTest {
//Ruleはフィールドを使うので
//別のテストで使ってないか注意必要
@Rule 
public TestName name = new TestName();
@Test
public void testA() {
assertEquals("testA", name.getMethodName());
}
@Test
public void testB() {
assertEquals("testB", name.getMethodName());
}
}



TestNameはどうなった?


  • テスト名をとれる@RuleのAPI

  • 消えます。

  • 代わりに、テストメソッドのパラメータから取れます。


    • テスト名含む各種情報も取れます。

    • TestInfo





テストにパラメータを渡せる


  • どんなパラメータを渡すかもカスタマイズ可能



    • ParameterResolver によるしくみ



  • 組み込みのResolver


    • TestInfoParameterResolver


    • TestReporterParameterResolver


      • テストでログを吐きたい時の委譲先






import org.junit.jupiter.api.TestInfo;

// importは適当に省略してます。
class TestInfoDemo {
@Test
@Tag("my tag")
void test1(TestInfo testInfo) {
assertTrue(testInfo.getTags().contains("my tag"));
}
@Test
void test2() { }
}



できるようになったこと :one:


  • テスト名を個別につけられるようになった。

import org.junit.jupiter.api.DisplayName;

import org.junit.jupiter.api.Test;
class DisplayNameDemo {
@Test
@DisplayName("なんとかてすと")
void testWithDisplayNameContainingSpaces() {
}
}



できるようになったこと :two:


  • テストクラスを内部クラスとする機能が標準になりました。


    • experimental ではなくなりました。

    • @Nested





できるようになったこと :three:



  • DynamicTest


    • テストを作る処理を書くことができる。


      • 動的にテストが作られる。







parameterized の代替

テスト対象メソッドに、テストデータを多数渡してテストしたい。


  • テストしたい処理

  • テストデータを作る処理

以上をラムダで書いておくと、JUnit5が組み合わせて実行してくれます。動的です。


// JUnit4 だとやっぱりフィールドを使うので、つかいにくい

@RunWith(Parameterized.class)
public class NoConstructorTest {
@Parameters
public static Iterable<Object[]> data() {
return Arrays.asList(new Object[][] { { 1, 1 }, { -2, 2 } });
}
@Parameter
public int fInput;
@Parameter(1)
public int fExpected;
@Test
public void testNantoka() {
assertEquals(fExpected, Math.abs(fInput));
}
}



JUnit5 M1 の個人的な感想


  • 普通の機能はそのまま。

  • experimentalで便利な機能を標準機能に昇格させる。

  • テストメソッドだけで機能が完結するような設計にする。



    • Rule の廃止。ExtendWithによる新設計





興味を持ったら。



  • https://github.com/junit-team/junit5-samples


    • サンプルプロジェクト

    • gradleのJUnit5プラグインとか試せます。

    • コードに絵文字が普通に入っているので環境によっては修正必要かも





IDE


  • IntelliJ IDEA 2016.2 がおすすめ。


    • テストクラス右クリックからテスト実行できます。

    • テストメソッドだと実行はできるが謎のエラーが出る。:disappointed_relieved:





JUnit4なプロジェクトの場合


  • @RunWith(JUnitPlatform.class)で、ツール側にはJUnit4のテストだとみせかけつつ、中身はJUnit5のテストを実行するとができる。


import static org.junit.jupiter.api.Assertions.fail;

import org.junit.jupiter.api.Test;
import org.junit.platform.runner.JUnitPlatform;
import org.junit.runner.RunWith;

@RunWith(JUnitPlatform.class)
public class JUnit4ClassDemo {
@Test
void succeedingTest() {
}
@Test
void failingTest() {
fail("Failing for failing's sake.");
}
}



今回触れてないこと


  • 例外を想定するテスト


    • 簡潔に書けるようになりました。


    • ExpectedExceptionというRuleは消えます。



  • Interface の defaultメソッドのテスト

  • 他にも色々あるとおもいます。多分。 :sweat_smile:



参考文献



ご清聴ありがとうございました! :bowtie: