JUnit 5: Szybki poradnik na start

JUnit to świetny framework do testowania jednostkowego w języku programowania Java. Jednakże czwarta wersja tego frameworka ma kilka niedociągnięć, takich jak problem z wieloma narzędziami do uruchamiania testów czy brak testów zależnych, co dla programistów oznacza trudności w tworzeniu testów integracyjnych.

Zespół JUnit 5 opublikował pierwszą ogólnodostępną wersję w dniu 10 września 2017. JUnit pozwala na wiele rodzajów testów i skupia się na Javie 8 i nowszych wersjach. W tym artykule pokażę Ci, jak zacząć pracę z nową wersją JUnit. Wychodzę z założenia, że znasz już JInit 4.12, Javę 8 i Maven.

Przegląd JUnit 5

JUnit 5 składa się z kilku modułów wywodzących się z trzech różnych podprojektów:

  • JUnit Platform odpowiada za uruchamianie frameworków testowych na JVM. Określa API silnika testowego, służące do tworzenia frameworku testowego uruchamianego na platformie. Funkcjonuje jako interfejs pomiędzy JUnit i klientami, takimi jak np. narzędzia kompilacji.
  • JUnit Jupiter udostępnia silnik testowy do uruchamiania testów opartych na Jupiter na platformie.
  • JUnit Vintage pozwala na uruchamianie testów opartych na JUnit 3 i JUnit 4 na platformie JUnit 5.

Instalacja JUnit 5

Aby uruchomić swój pierwszy test z JUnit 5, trzeba dodać następującą zależność do pliku pom.xml:

<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.0.2</version>
    <scope>test</scope>
</dependency>

Warto zaznaczyć, że w momencie uruchamiania JUnit 5 wymaga wersji Java 8 (lub nowszej).

Zmiany w adnotacjach

JUnit 5 wprowadza istotne zmiany dotyczące adnotacji:

  • Adnotacja @Before zmieniła się w @BeforeEach
  • Adnotacja @After zmieniła się w @AfterEach
  • Adnotacja @BeforeClass zmieniła się w @BeforeAll
  • Adnotacja @AfterClass zmieniła się w @AfterAll
  • Adnotacja @Ignore zmieniła się w @Disabled

Asercje i instrukcje warunkowe

JUnit 5 dodaje kilka nowych asercji, które świetnie nadają się do używania z lambdami Java 8. Metoda assertAll() może wykonać wszystkie asercje, niezależnie od jej wyników:

@Test
void shouldAddTwoPositiveNumbers() {
    assertAll(
            () -> assertEquals(4, calculator.add(2, 2)),
            () -> assertEquals(6, calculator.add(3, 3))
    );
}

Spodziewany parametr został usunięty z adnotacji @Test. Nadal możemy używać mechanizmu @Rule, by sprawdzić, czy przetestowana metoda zwraca wyjątek. Jednak mamy też nowy sposób na testowanie tego. Metoda assertThrows służy do sprawdzenia, czy dany wiersz testu powoduje wystąpienie wyjątku:

@Test
void shouldThrowExceptionWhenDivideByZero() {
    Throwable ex = assertThrows(IllegalArgumentException.class,
            () -> calculator.divide(2, 0));
    assertEquals("Cannot divide by zero", ex.getMessage());
}

Innym parametrem usuniętym z adnotacji @Test jest ograniczenie czasu wykonywania (timeout), które mogło kontrolować czas trwania testu. Lepszym sposobem na mierzenie czasu wykonywania testu są wprowadzone w JUnit 5 asercje assertTimeout iassertTimeoutPreemptively. Pierwsza czeka, aż testowana metoda dobiegnie do końca i zakończy się niepowodzeniem, jeśli wyznaczone ograniczenie czasu wykonywania zostanie przekroczone. Komunikat takiej asercji wskaże, o ile przekroczono dozwolony czas. Asercja assertTimeoutPreemptively zostaje przerwana natychmiast w momencie przekroczenia ograniczenia czasowego.

@Test
void shouldExecuteWithin10ms_waitForCompletion() {
    assertTimeout(ofMillis(10),
            () -> calculator.add(123, 456));
}

@Test
void shouldExecuteWithin10ms_failIfExceeded() {
    assertTimeoutPreemptively(ofMillis(10),
            () -> calculator.add(321, 654));
}

W JUnit 5 nie ma metody assertThat(), akceptującej matchery Hamcrest, znane z poprzednich wersji. Zamiast tego, programistów zachęca się do korzystania z wbudowanej obsługi matcherów dostarczanych przez biblioteki asercji innych firm, takie jak AssertJ, Hamcrest, Truth itp.

Instrukcje warunkowe są podobne do asercji z tym wyjątkiem, że muszą one pozostawać prawdziwe ˜– inaczej test zostanie przerwany. Instrukcje warunkowe wykorzystuje się do uruchamiania testów tylko w przypadku, gdy spełnione zostaną określone warunki.

@Test
void runOnWindows7Only() {
    assumeTrue("Windows 7".equals(System.getProperty("os.name")));
    // Test will be aborted if assumption is not met
    // ...
}

Nazwy wyświetlania

Klasy i metody testów mogą deklarować niestandardowe nazwy wyświetlania (zawierające spacje i znaki specjalne), które będą wyświetlane przez narzędzia uruchamiania testów i raporty testowe.

@Test
@DisplayName("2 + 2 addition test")
void testAddition() {
    assertEquals(4, calculator.add(2 , 2));
}

Raport testowy wyświetli nazwę testu zamiast nazwy metody, która często bywa zbyt długa i nieczytelna.

Podsumowanie

Opisałem tutaj podstawy obsługi JUnit 5. Jedno z głównych założeń JUnit 5 to ułatwienie wprowadzania udoskonaleń i ewolucji JUnit 5 w przyszłości. Obecna wersja pozwala na tworzenie bardziej czytelnych i intuicyjnych testów. Jest obsługiwana przez Javę 8 i oferuje wiele nowych możliwości testowania. W następnym artykule przyjrzymy się nowym funkcjom JUnit 5, które sprawiają, że ponownie konkuruje z bardziej zaawansowanym TestNG.

Poznaj mageek of j‑labs i daj się zadziwić, jak może wyglądać praca z j‑People!

Skontaktuj się z nami