Panduan untuk JUnit 5

1. Gambaran keseluruhan

JUnit adalah salah satu kerangka pengujian unit yang paling popular di ekosistem Jawa. Versi JUnit 5 mengandungi sejumlah inovasi menarik, dengan tujuan untuk menyokong ciri baru di Java 8 dan ke atas , serta memungkinkan pelbagai gaya ujian.

2. Pergantungan Maven

Menyiapkan JUnit 5.x.0 cukup mudah, kita perlu menambahkan kebergantungan berikut ke pom.xml kami :

 org.junit.jupiter junit-jupiter-engine 5.1.0 test 

Penting untuk diperhatikan bahawa versi ini memerlukan Java 8 berfungsi .

Lebih-lebih lagi, kini ada sokongan langsung untuk menjalankan ujian Unit di Platform JUnit di Eclipse dan juga IntelliJ. Anda tentu saja dapat menjalankan ujian menggunakan tujuan Ujian Maven.

Sebaliknya, IntelliJ menyokong JUnit 5 secara lalai. Oleh itu, menjalankan JUnit 5 di IntelliJ cukup mudah, cukup Klik kanan -> Jalankan, atau Ctrl-Shift-F10.

3. Senibina

JUnit 5 terdiri daripada beberapa modul yang berbeza dari tiga sub-projek yang berbeza:

3.1. Platform JUnit

Platform ini bertanggungjawab untuk melancarkan kerangka ujian di JVM. Ia menentukan antara muka yang stabil dan kuat antara JUnit dan pelanggannya seperti alat bina.

Objektif terakhir adalah bagaimana pelanggannya dapat disatukan dengan mudah dengan JUnit dalam menemui dan melaksanakan ujian.

Ini juga menentukan TestEngine API untuk mengembangkan kerangka pengujian yang berjalan di platform JUnit. Dengan itu, anda boleh memasukkan perpustakaan ujian pihak ke-3, terus ke JUnit, dengan menerapkan TestEngine tersuai.

3.2. JUnit Musytari

Modul ini merangkumi model dan model pelanjutan baru untuk ujian penulisan di JUnit 5. Anotasi baru berbanding dengan JUnit 4 adalah:

  • @TestFactory - menunjukkan kaedah yang merupakan kilang ujian untuk ujian dinamik
  • @DisplayName - menentukan nama paparan tersuai untuk kelas ujian atau kaedah ujian
  • @Nested - menunjukkan bahawa kelas beranotasi adalah kelas ujian bersarang dan tidak statik
  • @Tag - menyatakan tag untuk ujian penapisan
  • @ExtendWith - ia digunakan untuk mendaftarkan pelanjutan khas
  • @BeforeEach - menunjukkan bahawa kaedah anotasi akan dilaksanakan sebelum setiap kaedah ujian (sebelumnya @Sebelumnya )
  • @AfterEach - menunjukkan bahawa kaedah anotasi akan dilaksanakan setelah setiap kaedah ujian (sebelumnya @After )
  • @BeforeAll - menunjukkan bahawa kaedah anotasi akan dilaksanakan sebelum semua kaedah ujian di kelas semasa (sebelumnya @BeforeClass )
  • @AfterAll - menunjukkan bahawa kaedah anotasi akan dilaksanakan setelah semua kaedah ujian di kelas semasa (sebelumnya @AfterClass )
  • @Disable - ia digunakan untuk melumpuhkan kelas atau kaedah ujian (sebelumnya @ Abaikan )

3.3. JUnit Vintage

Menyokong menjalankan ujian berdasarkan JUnit 3 dan JUnit 4 pada platform JUnit 5.

4. Anotasi Asas

Untuk membincangkan anotasi baru, kami membahagikan bahagian tersebut ke dalam kumpulan berikut, yang bertanggungjawab untuk pelaksanaan: sebelum ujian, semasa ujian (pilihan) dan selepas ujian:

4.1. @BeforeAll dan @BeforeEach

Berikut adalah contoh kod mudah yang akan dilaksanakan sebelum kes ujian utama:

@BeforeAll static void setup() { log.info("@BeforeAll - executes once before all test methods in this class"); } @BeforeEach void init() { log.info("@BeforeEach - executes before each test method in this class"); }

Penting untuk diperhatikan ialah kaedah dengan anotasi @BeforeAll perlu statik, jika tidak, kod tidak akan disusun.

4.2. @DisplayName dan @Disabled

Mari beralih ke kaedah pilihan-ujian baru:

@DisplayName("Single test successful") @Test void testSingleSuccessTest() { log.info("Success"); } @Test @Disabled("Not implemented yet") void testShowSomething() { }

Seperti yang dapat kita lihat, kita dapat mengubah nama paparan atau menonaktifkan kaedah dengan komen, menggunakan anotasi baru.

4.3. @AfterEach dan @AfterAll

Akhirnya, mari kita bincangkan kaedah yang berkaitan dengan operasi selepas pelaksanaan ujian:

@AfterEach void tearDown() { log.info("@AfterEach - executed after each test method."); } @AfterAll static void done() { log.info("@AfterAll - executed after all test methods."); }

Harap maklum bahawa kaedah dengan @AfterAll perlu juga kaedah statik.

5. Tegasan dan Andaian

JUnit 5 cuba memanfaatkan sepenuhnya ciri-ciri baru dari Java 8, terutamanya ungkapan lambda.

5.1. Ketegasan

Tegasan telah dipindahkan ke org.junit.jupiter.api. Tegasan dan telah diperbaiki dengan ketara. Seperti yang telah disebutkan sebelumnya, kini anda dapat menggunakan lambdas dalam pernyataan:

@Test void lambdaExpressions() { assertTrue(Stream.of(1, 2, 3) .stream() .mapToInt(i -> i) .sum() > 5, () -> "Sum should be greater than 5"); }

Walaupun contoh di atas adalah remeh, satu kelebihan menggunakan ungkapan lambda untuk mesej penegasan adalah bahawa ia dinilai dengan malas, yang dapat menjimatkan masa dan sumber jika pembinaan mesej itu mahal.

It is also now possible to group assertions with assertAll() which will report any failed assertions within the group with a MultipleFailuresError:

 @Test void groupAssertions() { int[] numbers = {0, 1, 2, 3, 4}; assertAll("numbers", () -> assertEquals(numbers[0], 1), () -> assertEquals(numbers[3], 3), () -> assertEquals(numbers[4], 1) ); }

This means it is now safer to make more complex assertions, as you will be able to pinpoint the exact location of any failure.

5.2. Assumptions

Assumptions are used to run tests only if certain conditions are met. This is typically used for external conditions that are required for the test to run properly, but which are not directly related to whatever is being tested.

You can declare an assumption with assumeTrue(), assumeFalse(), and assumingThat().

@Test void trueAssumption() { assumeTrue(5 > 1); assertEquals(5 + 2, 7); } @Test void falseAssumption() { assumeFalse(5  assertEquals(2 + 2, 4) ); }

If an assumption fails, a TestAbortedException is thrown and the test is simply skipped.

Assumptions also understand lambda expressions.

6. Exception Testing

There are two ways of exception testing in JUnit 5. Both of them can be implemented by using assertThrows() method:

@Test void shouldThrowException() { Throwable exception = assertThrows(UnsupportedOperationException.class, () -> { throw new UnsupportedOperationException("Not supported"); }); assertEquals(exception.getMessage(), "Not supported"); } @Test void assertThrowsException() { String str = null; assertThrows(IllegalArgumentException.class, () -> { Integer.valueOf(str); }); }

The first example is used to verify more detail of the thrown exception and the second one just validates the type of exception.

7. Test Suites

To continue the new features of JUnit 5, we will try to get to know the concept of aggregating multiple test classes in a test suite so that we can run those together. JUnit 5 provides two annotations: @SelectPackages and @SelectClasses to create test suites.

Keep in mind that at this early stage most IDEs do not support those features.

Let's have a look at the first one:

@RunWith(JUnitPlatform.class) @SelectPackages("com.baeldung") public class AllUnitTest {}

@SelectPackage is used to specify the names of packages to be selected when running a test suite. In our example, it will run all test. The second annotation, @SelectClasses, is used to specify the classes to be selected when running a test suite:

@RunWith(JUnitPlatform.class) @SelectClasses({AssertionTest.class, AssumptionTest.class, ExceptionTest.class}) public class AllUnitTest {}

For example, above class will create a suite contains three test classes. Please note that the classes don't have to be in one single package.

8. Dynamic Tests

The last topic that we want to introduce is JUnit 5 Dynamic Tests feature, which allows to declare and run test cases generated at run-time. In contrary to the Static Tests which defines fixed number of test cases at the compile time, the Dynamic Tests allow us to define the tests case dynamically in the runtime.

Dynamic tests can be generated by a factory method annotated with @TestFactory. Let's have a look at the code example:

@TestFactory public Stream translateDynamicTestsFromStream() { return in.stream() .map(word -> DynamicTest.dynamicTest("Test translate " + word, () -> { int id = in.indexOf(word); assertEquals(out.get(id), translate(word)); }) ); }

This example is very straightforward and easy to understand. We want to translate words using two ArrayList, named in and out, respectively. The factory method must return a Stream, Collection, Iterable, or Iterator. In our case, we choose Java 8 Stream.

Please note that @TestFactory methods must not be private or static. The number of tests is dynamic, and it depends on the ArrayList size.

9. Conclusion

Penulisan ini adalah gambaran ringkas mengenai perubahan yang akan datang dengan JUnit 5.

Kita dapat melihat bahawa JUnit 5 mempunyai perubahan besar dalam senibina yang berkaitan dengan pelancar platform, integrasi dengan alat binaan, IDE, kerangka ujian Unit lain, dan lain-lain. Lebih-lebih lagi, JUnit 5 lebih terintegrasi dengan Java 8, terutamanya dengan konsep Lambdas dan Stream .

Contoh yang digunakan dalam artikel ini boleh didapati dalam projek GitHub.