クイックJUnitとTestNGの比較

1概要

JUnitとTestNGは、間違いなくJavaエコシステムで最も人気のある2つの単体テストフレームワークです。 JUnitはTestNG自体を奮い立たせますが、それはその独特の機能を提供し、JUnitとは異なり、機能的でより高レベルのテストに機能します。

この記事では、** これらのフレームワークについて、その機能と一般的な使用例を説明しながら説明します。

2テスト設定

テストケースを書いている間、テスト実行の前にいくつかの設定や初期化命令を実行する必要があります。両方のフレームワークでこれらを評価しましょう。

  • JUnitは、各メソッドとクラスの前後に2つのレベルで初期化とクリーンアップを提供します。

public class SummationServiceTest {

    private static List<Integer> numbers;

    @BeforeAll
    public static void initialize() {
        numbers = new ArrayList<>();
    }

    @AfterAll
    public static void tearDown() {
        numbers = null;
    }

    @BeforeEach
    public void runBeforeEachTest() {
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
    }

    @AfterEach
    public void runAfterEachTest() {
        numbers.clear();
    }

    @Test
    public void givenNumbers__sumEquals__thenCorrect() {
        int sum = numbers.stream().reduce(0, Integer::sum);
        assertEquals(6, sum);
    }
}

この例ではJUnit 5を使用していることに注意してください。以前のJUnit 4バージョンでは、 @ BeforeEach および @AfterEachと同等の @ Before および @ After アノテーションを使用する必要があります。 同様に、 @ BeforeAll @ AfterAll は、JUnit 4の @ BeforeClass @AfterClassの代わりになります。

JUnitと同様に、 TestNGはメソッドおよびクラスレベル での初期化とクリーンアップも提供します。 @ BeforeClass @ AfterClass はクラスレベルで同じままですが、メソッドレベルのアノテーションは@ BeforeMethod と__ @ AfterMethodです。

@BeforeClass
public void initialize() {
    numbers = new ArrayList<>();
}

@AfterClass
public void tearDown() {
    numbers = null;
}

@BeforeMethod
public void runBeforeEachTest() {
    numbers.add(1);
    numbers.add(2);
    numbers.add(3);
}

@AfterMethod
public void runAfterEachTest() {
    numbers.clear();
}
  • TestNGは、スイートおよびグループレベルでの設定のために、 @ BeforeSuite、@AfterSuite、@BeforeGroupおよび@AfterGroup アノテーションも提供します。

@BeforeGroups("positive__tests")
public void runBeforeEachGroup() {
    numbers.add(1);
    numbers.add(2);
    numbers.add(3);
}

@AfterGroups("negative__tests")
public void runAfterEachGroup() {
    numbers.clear();
}

また、TestNG XML設定ファイルの <test> タグに含まれるテストケースの前後に設定が必要な場合は、 @ BeforeTest と@ AfterTest を使用できます。

<test name="test setup">
    <classes>
        <class name="SummationServiceTest">
            <methods>
                <include name="givenNumbers__sumEquals__thenCorrect"/>
            </methods>
        </class>
    </classes>
</test>

@ BeforeClass および @ AfterClass メソッドの宣言は、JUnitでは静的である必要があります。比較すると、TestNGメソッドの宣言にはこれらの制約がありません。

3テストを無視する

  • どちらのフレームワークもテストケースを無視することをサポートしています** が、それらはまったく異なっています。 JUnitは @ Ignore アノテーションを提供しています:

@Ignore
@Test
public void givenNumbers__sumEquals__thenCorrect() {
    int sum = numbers.stream().reduce(0, Integer::sum);
    Assert.assertEquals(6, sum);
}

TestNGは、ブール値 true または false を持つパラメーター "enabled"と共に @ Test を使用します。

@Test(enabled=false)
public void givenNumbers__sumEquals__thenCorrect() {
    int sum = numbers.stream.reduce(0, Integer::sum);
    Assert.assertEquals(6, sum);
}

4一緒にテストを実行する

JUnit とTestNGの両方でコレクションとしてテストを一緒に実行することは可能ですが、彼らは異なる方法でそれを行います。

  • @ RunWith、 @ SelectPackages 、および @ SelectClasses アノテーションを使用してテストケースをグループ化し、それらを JUnit 5 でスイートとして実行できます。 1回のテスト

__Suite内で異なるパッケージのテストケースをまとめて実行する場合は、 @ SelectPackages__アノテーションが必要です。

@RunWith(JUnitPlatform.class)
@SelectPackages({ "org.baeldung.java.suite.childpackage1", "org.baeldung.java.suite.childpackage2" })
public class SelectPackagesSuiteUnitTest {

}

特定のテストクラスを一緒に実行したい場合は、 JUnit 5 @ SelectClasses を介して柔軟性を提供します。

@RunWith(JUnitPlatform.class)
@SelectClasses({Class1UnitTest.class, Class2UnitTest.class})
public class SelectClassesSuiteUnitTest {

}

以前は JUnit 4 を使用していましたが、 __ @ Suite __アノテーションを使用して複数のテストをまとめて実行することができました。

@RunWith(Suite.class)
@Suite.SuiteClasses({ RegistrationTest.class, SignInTest.class })
public class SuiteTest {

}
  • TestNGでは、XMLファイルを使用してテストをグループ化できます。**

<suite name="suite">
    <test name="test suite">
        <classes>
            <class name="com.baeldung.RegistrationTest"/>
            <class name="com.baeldung.SignInTest"/>
        </classes>
    </test>
</suite>

これは RegistrationTest SignInTest が一緒に実行されることを示します。

クラスをグループ化することとは別に、TestNGは@ Test(groups =” groupName”) アノテーションを使用してメソッドをグループ化することもできます。

@Test(groups = "regression")
public void givenNegativeNumber__sumLessthanZero__thenCorrect() {
    int sum = numbers.stream().reduce(0, Integer::sum);
    Assert.assertTrue(sum < 0);
}

XMLを使用してグループを実行しましょう。

<test name="test groups">
    <groups>
        <run>
            <include name="regression"/>
        </run>
    </groups>
    <classes>
        <class
          name="com.baeldung.SummationServiceTest"/>
    </classes>
</test>

これはgroup regression でタグ付けされたテストメソッドを実行します。

5例外をテストする

アノテーションを使用して例外をテストするための機能はJUnitとTestNGの両方で利用可能です。

まず、例外をスローするメソッドを使ってクラスを作成しましょう。

public class Calculator {
    public double divide(double a, double b) {
        if (b == 0) {
            throw new DivideByZeroException("Divider cannot be equal to zero!");
        }
        return a/b;
    }
}

JUnit 5 では、 __ assertThrows __APIを使用して例外をテストできます。

@Test
public void whenDividerIsZero__thenDivideByZeroExceptionIsThrown() {
    Calculator calculator = new Calculator();
    assertThrows(DivideByZeroException.class, () -> calculator.divide(10, 0));
}

_JUnit 4では、 weはテストAPIの上で _ @ Test(expected = DivideByZeroException.class)を使用することでこれを実現できます。

そしてTestNGを使えば、同じことを実装することもできます。

@Test(expectedExceptions = ArithmeticException.class)
public void givenNumber__whenThrowsException__thenCorrect() {
    int i = 1/0;
}

この機能は、テストの一部であるコードの一部からどの例外がスローされるかを意味します。

6. パラメータ化テスト

パラメータ化された単体テストは、いくつかの条件下で同じコードをテストするのに役立ちます。パラメータ化された単体テストの助けを借りて、我々はいくつかのデータソースからデータを取得するテストメソッドを設定することができます。主な考え方は、単体テスト方法を再利用可能にし、異なる入力セットでテストすることです。

  • JUnit 5 では、テストメソッドが設定されたソースから直接データ引数を消費するという利点があります** デフォルトでは、JUnit 5は以下のようないくつかの source アノテーションを提供します。

  • @ ValueSource: これをtypeの値の配列と共に使うことができます

Short、Byte、Int、Long、Float、Double、Char、 、および String:

@ParameterizedTest
@ValueSource(strings = { "Hello", "World" })
void givenString__TestNullOrNot(String word) {
    assertNotNull(word);
}
  • __ @ EnumSource - テストへのパラメータとして Enum __定数を渡します。

方法:

@ParameterizedTest
@EnumSource(value = PizzaDeliveryStrategy.class, names = {"EXPRESS", "NORMAL"})
void givenEnum__TestContainsOrNot(PizzaDeliveryStrategy timeUnit) {
    assertTrue(EnumSet.of(PizzaDeliveryStrategy.EXPRESS, PizzaDeliveryStrategy.NORMAL).contains(timeUnit));
}
  • __ @ MethodSource - ストリームを生成する外部メソッドを使用します。

static Stream<String> wordDataProvider() {
    return Stream.of("foo", "bar");
}

@ParameterizedTest
@MethodSource("wordDataProvider")
void givenMethodSource__TestInputStream(String argument) {
    assertNotNull(argument);
}
  • @ CsvSource – は、パラメータのソースとしてCSV値を使用します。

@ParameterizedTest
@CsvSource({ "1, Car", "2, House", "3, Train" })
void givenCSVSource__TestContent(int id, String word) {
    assertNotNull(id);
    assertNotNull(word);
}

同様に、クラスパスからCSVファイルを読み込む必要がある場合は __ @ CsvFileSource、カスタム再利用可能な ArgumentsProvider. を指定する場合は @ ArgumentSource__など、他のソースもあります。

JUnit 4 では、テストクラスにパラメータ化されたクラスにするためにテストクラスに @ RunWith と注釈を付け、単体テスト用にパラメータ値を表すために @ Parameter を付ける必要があります。

  • TestNGでは、@ Parameter または @ DataProvider アノテーションを使用してテストをパラメータ化することができます。

@Test
@Parameters({"value", "isEven"})
public void
  givenNumberFromXML__ifEvenCheckOK__thenCorrect(int value, boolean isEven) {
    Assert.assertEquals(isEven, value % 2 == 0);
}

そしてXMLファイルでデータを提供します。

<suite name="My test suite">
    <test name="numbersXML">
        <parameter name="value" value="1"/>
        <parameter name="isEven" value="false"/>
        <classes>
            <class name="baeldung.com.ParametrizedTests"/>
        </classes>
    </test>
</suite>

XMLファイルの情報を使用するのは簡単で便利ですが、場合によってはもっと複雑なデータを提供する必要があります。

これには、メソッドをテストするために複雑なパラメータ型をマップできるようにする @ DataProvider アノテーションを使用できます。

これは、プリミティブデータ型に @ DataProvider を使用する例です。

@DataProvider(name = "numbers")
public static Object[][]evenNumbers() {
    return new Object[][]{{1, false}, {2, true}, {4, true}};
}

@Test(dataProvider = "numbers")
public void givenNumberFromDataProvider__ifEvenCheckOK__thenCorrect
  (Integer number, boolean expected) {
    Assert.assertEquals(expected, number % 2 == 0);
}

そして __ @ DataProvider __オブジェクト用:

@Test(dataProvider = "numbersObject")
public void givenNumberObjectFromDataProvider__ifEvenCheckOK__thenCorrect
  (EvenNumber number) {
    Assert.assertEquals(number.isEven(), number.getValue() % 2 == 0);
}

@DataProvider(name = "numbersObject")
public Object[][]parameterProvider() {
    return new Object[][]{{new EvenNumber(1, false)},
      {new EvenNumber(2, true)}, {new EvenNumber(4, true)}};
}

同様に、テスト対象の特定のオブジェクトを作成し、データプロバイダを使用して返すことができます。 Springのようなフレームワークと統合するときに便利です。

TestNGでは、 @ DataProvider メソッドは静的である必要はないので、同じテストクラスで複数のデータプロバイダメソッドを使用できます。

7. テストタイムアウト

タイムアウトテストとは、特定の期間内に実行が完了しなかった場合にテストケースが失敗することを意味します。 JUnitとTestNGの両方がタイムアウトテストをサポートします。 JUnit 5 では、タイムアウトテストを____:と書くことができます。

@Test
public void givenExecution__takeMoreTime__thenFail() throws InterruptedException {
    Assertions.assertTimeout(Duration.ofMillis(1000), () -> Thread.sleep(10000));
}

JUnit 4 とTestNGでは、@ __ Test(timeout = 1000)を使用して同じテストを実行できます。

@Test(timeOut = 1000)
public void givenExecution__takeMoreTime__thenFail() {
    while (true);
}

8依存テスト

TestNGは依存関係テストをサポートします。つまり、一連のテストメソッドでは、最初のテストが失敗した場合、後続のすべての依存テストはスキップされ、JUnitの場合のように失敗とマークされることはありません。

電子メールを検証する必要があるシナリオを見てみましょう。成功した場合は、ログインに進みます。

@Test
public void givenEmail__ifValid__thenTrue() {
    boolean valid = email.contains("@");
    Assert.assertEquals(valid, true);
}

@Test(dependsOnMethods = {"givenEmail__ifValid__thenTrue"})
public void givenValidEmail__whenLoggedIn__thenTrue() {
    LOGGER.info("Email {} valid >> logging in", email);
}

** 9テスト実行順序

  • JUnit 4またはTestNGでテストメソッドが実行される明示的な暗黙の順序はありません** メソッドはJava Reflection APIによって返されるとおりに呼び出されるだけです。 JUnit 4以降では、より確定的な順序を使用していますが予測できません。

より細かく制御するために、テストクラスに @ FixMethodOrder アノテーションを付けてメソッドソーターに言及します。

@FixMethodOrder(MethodSorters.NAME__ASCENDING)
public class SortedTests {

    @Test
    public void a__givenString__whenChangedtoInt__thenTrue() {
        assertTrue(
          Integer.valueOf("10") instanceof Integer);
    }

    @Test
    public void b__givenInt__whenChangedtoString__thenTrue() {
        assertTrue(
          String.valueOf(10) instanceof String);
    }

}

MethodSorters.NAME ASCENDING パラメーターは、メソッド名順にメソッドをソートします(辞書順)。このソーターの他に、 MethodSorter.DEFAULTとMethodSorter.JVMもあります。

TestNGはテストメソッドの実行順に制御する方法をいくつか提供します。 @ Test アノテーションに priority パラメーターを指定します。

@Test(priority = 1)
public void givenString__whenChangedToInt__thenCorrect() {
    Assert.assertTrue(
      Integer.valueOf("10") instanceof Integer);
}

@Test(priority = 2)
public void givenInt__whenChangedToString__thenCorrect() {
    Assert.assertTrue(
      String.valueOf(23) instanceof String);
}

優先度は優先度に基づいてテストメソッドを呼び出すが、あるレベルのテストが次の優先度レベルを呼び出す前に完了することを保証するものではないことに注意してください。

TestNGで機能的なテストケースを書いている間、実行順序がすべてのテスト実行で同じでなければならない相互依存テストがあるかもしれません。これを実現するには、前のセクションで見たように、 dependsOnMethods パラメーターを@ Test アノテーションに使用する必要があります。

10.カスタムテスト名

デフォルトでは、テストを実行するたびに、テストクラスとテストメソッド名がコンソールまたはIDEに表示されます。 JUnit 5 には、@ @ DisplayName__アノテーションを使用してクラスおよびテストメソッドのカスタム記述名を指定できる独自の機能があります。

この注釈はテスト上の利点を提供するものではありませんが、技術者以外の人にとってもテスト結果を読みやすく理解しやすくなります。

@ParameterizedTest
@ValueSource(strings = { "Hello", "World" })
@DisplayName("Test Method to check that the inputs are not nullable")
void givenString__TestNullOrNot(String word) {
    assertNotNull(word);
}

テストを実行するたびに、メソッド名の代わりに表示名が表示されます。

今のところ、 TestNG にはカスタム名を指定する方法はありません。

11結論

JUnitもTestNGも、Javaエコシステムでテストを行うための最新のツールです。

この記事では、これら2つのテスト・フレームワークのそれぞれを使用してテストを作成するさまざまな方法について簡単に説明しました。

すべてのコードスニペットの実装はhttps://github.com/eugenp/tutorials/tree/master/testing-modules/testng[TestNG]およびhttps://github.com/eugenp/tutorials/tree/にあります。 master/testing-modules/junit-5[junit-5 Githubプロジェクト]。