JUnit5でパラメータテストを行う-その1-(@ValueSource, @CsvSource, @MethodSource)

エキサイト株式会社メディア事業部エンジニア佐々木です。JUnit5からパラメータテストが大変しやすくなったので、ご紹介します。

前提

環境は下記になります。

# Java
openjdk version "19.0.1" 2022-10-18
OpenJDK Runtime Environment Corretto-19.0.1.10.1 (build 19.0.1+10-FR)
OpenJDK 64-Bit Server VM Corretto-19.0.1.10.1 (build 19.0.1+10-FR, mixed mode, sharing)

# JUnit
org.junit.jupiter:junit-jupiter-api:5.8.1

build.gradle

plugins {
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    testImplementation(platform('org.junit:junit-bom:5.9.1'))
    testImplementation('org.junit.jupiter:junit-jupiter')
}

test {
    useJUnitPlatform()
}

@ValueSource

@ValueSourceアノテーションを使うと、引数に値を動的に入れてテストを実行してくれます。@ValueSourceに指定できるのは、String, int, long, doubleになります。

public class MainTest {

    private List<String> expectList;

    @BeforeEach
    void setup() {
        this.expectList = List.of("a", "b", "c");
    }

    @ParameterizedTest(name = "Value Source {0} test") // このように記述すると、{0} の部分に値が入ります
    @ValueSource(strings = {"a", "b", "c", "d"})
    void testValueSource(String s) {
        System.out.println("Value Source " + s + " test");
        Assertions.assertThat(s).isIn(this.expectList);
    }
}


実行
==

Value Source a test
Value Source b test
Value Source c test
Value Source d test

// 最後のdに関しては、期待値の中に入っていないので下記のようなFailedのエラーが出力されます。

Expecting actual:
  "d"
to be in:
  ["a", "b", "c"]

java.lang.AssertionError: 
Expecting actual:
  "d"
to be in:
  ["a", "b", "c"]

実行すると@ValueSourceで指定した値が1つずつ入ってテストが実行しているのがわかります。

@CsvSource

@ValueSourceは、引数1つまでしか指定できないので、2つ以上の場合は@CsvSourceを使用します。アノテーションにカンマ区切り文字列を指定して渡していきます。

public class MainTest {
 
   @ParameterizedTest
    @CsvSource(value = {"a,1", "b,2"})
    void testCsvSource(String s, int i) {
        Assertions.assertThat(s).isEqualTo("a");
        Assertions.assertThat(i).isEqualTo(1);
    }
}

実行結果
==

Csv Source s: test1 i: 1
Csv Source s: test2 i: 2


// 2つめの引数では通らないようにテストしているので下記のようなFailedのエラーが出力れます。
expected: "test1"
 but was: "test2"
org.opentest4j.AssertionFailedError: 
expected: "test1"
 but was: "test2"

==

実行すると、文字列の中身がカンマ区切りで分割され、メソッドの引数に値が入り実行されています。

@MethodSource

@ValueSource@CsvSourceは単純な値のみを指定できますが、オブジェクトを引数にとれません。@MethodSourceは、メソッドを指定して、戻り値をオブジェクトにしてテストに渡すことができます。

public class MainTest {
    @ParameterizedTest
    @MethodSource("getItem") // メソッド名は文字列で渡します。
    void testArgumentSource(Item i) {

        
        Item test = new Item(1, "test");
        Assertions.assertThat(i).isEqualTo(test);
    }

    static Stream<Item> getItem() {
        return Stream.of(
                new Item(1, "test")
                , new Item(2, "test2")
        );
    }

    @RequiredArgsConstructor
    @Getter
    @EqualsAndHashCode
    private static class Item {
        private final int id;
        private final String name;
    }
}

実行結果
==
Item i:MainTest.Item(id=1, name=test)
Item i:MainTest.Item(id=2, name=test2)

// 2つめのオブジェクトでは通らないようにテストしているので下記のようなFailedのエラーが出力れます。
expected: MainTest.Item(id=1, name=test)
 but was: MainTest.Item(id=2, name=test2)
org.opentest4j.AssertionFailedError: 
expected: MainTest.Item(id=1, name=test)
 but was: MainTest.Item(id=2, name=test2)
==

@MethodSourceで指定するメソッドは、staticメソッドで戻り値は、Stream<>でなければ動きません。実行してみると、Stream.of()の中で生成したオブジェクトの個数分、テストが実行されているのがわかります。

まとめ

JUnit5だと結構簡単にパラメータテストが書けますね。ループ回したりしなくて良いので見通しもよく、テストコードを書くのに集中できます。どんどん活用していきましょう。JUnit5で用意されているパラメータテストのアノテーションはあといくつかありますが、別のエントリーで書くことにします。

最後に

エキサイトではフロントエンジニア、バックエンドエンジニア、アプリエンジニアを随時募集しております。長期インターンも歓迎していますので、興味があれば連絡いただければと思います。 カジュアル面談はこちらになります! meety.net

募集職種一覧はこちらになります!(カジュアルからもOK) www.wantedly.com