クイックJUnitとTestNGの比較

JUnitとTestNGの簡単な比較

1. 概要

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

この投稿では、we’ll discuss and compare these frameworks by covering their features and common use casesです。

2. テスト設定

テストケースの作成中、テストの実行前にいくつかの構成または初期化命令を実行し、テストの完了後にクリーンアップを実行する必要があることがよくあります。 両方のフレームワークでこれらを評価してみましょう。

JUnit offers initialization and cleanup at two levels, before and after each method and class.メソッドレベルで@BeforeEach@AfterEachアノテーション、クラスレベルで@BeforeAll@AfterAllのアノテーションがあります。

public class SummationServiceTest {

    private static List 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を使用していることに注意してください。 以前のJUnit4バージョンでは、@BeforeEachおよび @AfterEach. と同等の@Beforeおよび@Afterアノテーションを使用する必要がありました。同様に、@BeforeAllおよび@AfterAll)は、JUnit 4の@BeforeClass@AfterClass.の代わりになります

JUnitと同様に、TestNG also provides initialization and cleanup at the method and class level@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 and @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を使用できます。


    
        
            
                
            
        
    

@BeforeClassおよび@AfterClassメソッドの宣言はJUnitで静的でなければならないことに注意してください。 比較すると、TestNGメソッド宣言にはこれらの制約はありません。

3. テストを無視する

Both frameworks support ignoring test casesですが、まったく異なる方法で行います。 JUnitは、@Ignoreアノテーションを提供します。

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

TestNGは、ブール値trueまたはfalseでパラメーターが「有効」になっている@Testを使用します。

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

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

JUnitとTestNGの両方で、コレクションとして一緒にテストを実行できますが、実行方法は異なります。

We can use the @RunWith, @SelectPackages, and @SelectClasses annotations to group test cases and run them as a suite in JUnit 5.スイートは、グループ化して単一のテストとして実行できるテストケースのコレクションです。

異なるパッケージのテストケースをグループ化してSuite 内で一緒に実行する場合は、@SelectPackagesアノテーションが必要です。

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

}

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

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

}

以前はJUnit 4を使用していましたが、@Suite annotationを使用して複数のテストをグループ化して実行しました。

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

}

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


    
        
            
            
        
    

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

クラスのグループ化とは別に、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を使用してグループを実行しましょう。


    
        
            
        
    
    
        
    

これにより、グループ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, では、テストAPIで@Test(expected = DivideByZeroException.class) overを使用してこれを実現できます。

また、TestNGを使用して、同じものを実装することもできます。

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

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

6. パラメータ化されたテスト

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

In JUnit 5, we have the advantage of test methods consuming data arguments directly from the configured source.デフォルトでは、JUnit 5は次のようないくつかのsourceアノテーションを提供します。

  • @ValueSource:これは、タイプ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 – pは、ストリームを生成する外部メソッドを評価します。

static Stream 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を使用して単体テストのパラメーター値を示す必要があります。

In TestNG, we can parametrize tests using @Parameter or @DataProvider annotations. XMLファイルの使用中に、テストメソッドに@Parameter:アノテーションを付けます

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

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


    
        
        
        
            
        
    

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. テスト実行の順序

There is no defined implicit order in which test methods will get executed in JUnit 4 or TestNG.メソッドは、Java ReflectionAPIによって返されるように呼び出されます。 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 and MethodSorter.JVM as well.があります

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);
}

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

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

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つのテストフレームワークのそれぞれでテストを作成するさまざまな方法について簡単に説明しました。

すべてのコードスニペットの実装は、TestNGjunit-5 Github projectにあります。