JUnit 5 – Testy powtarzalne
Jedną z cech testów jednostkowych jest ich powtarzalność. Najlepiej, jeżeli powtórzenie jest wykonywane automatycznie. Jednak nie o tym dziś będziemy mówić. Czasami pisząc testy, musimy uwzględnić, że nasz kod uruchamiany jest w środowisku wielowątkowym. Nie ma w tym nic nadzwyczajnego. Podobnie jest w przypadku, gdy tworzymy testy, których zadaniem jest jakiegoś kodu w wielu kopiach jednocześnie. Dla mnie wzorcową implementacją tego zachowania jest TestNG:
Listing 1. Testy powtarzalne w TestNG
@Test
public class FizzBuzzTestNGRepeatedTest {
private FizzBuzz sut;
@BeforeTest
public void setup() {
sut = new FizzBuzz();
}
@Test(invocationCount = 100, threadPoolSize = 4)
public void shouldReturnFizzBuzzIfDiv3And5() throws Exception {
assertEquals("FizzBuzz", sut.fizzBuzz(15));
}
@Test(invocationCount = 100, threadPoolSize = 4)
public void shouldReturnBuzzIfDiv5() throws Exception {
assertEquals("Buzz", sut.fizzBuzz(5));
}
@Test(invocationCount = 100, threadPoolSize = 4)
public void shouldReturnFizzIfDiv3() throws Exception {
assertEquals("Fizz", sut.fizzBuzz(3));
}
@Test(invocationCount = 100, threadPoolSize = 4)
public void shouldReturnVal() throws Exception {
assertEquals("2", sut.fizzBuzz(2));
}
}
Mamy tu dość dużą elastyczność w konfiguracji testów. Po pierwsze za pomocą parametru invocationCount, możemy określić, ile testów ma zostać wykonanych. Po drugie parametr threadPoolSize pozwala nam na skonfigurowanie liczby wątków użytych do uruchomienia testów.
W przypadku JUnit 5 wygląda to trochę inaczej. Po pierwsze wprowadzono adnotację @RepeatedTest, która pozwala na określenie liczby powtórzeń. Adnotacja ta zachowuje się jak specjalizowana @Test.
Listing 2. Testy powtarzalne w JUnit 5
public class FizzBuzzJUnit5RepeatedTest {
private FizzBuzz sut;
@BeforeEach
public void setup() {
sut = new FizzBuzz();
}
@RepeatedTest(value = 100, name = "Repetition {currentRepetition} of {totalRepetition}")
public void shouldReturnFizzBuzzIfDiv3And5() throws Exception {
assertEquals("FizzBuzz", sut.fizzBuzz(15));
}
@RepeatedTest(value = 100, name = "Repetition {currentRepetition} of {totalRepetition}")
public void shouldReturnBuzzIfDiv5() throws Exception {
assertEquals("Buzz", sut.fizzBuzz(5));
}
@RepeatedTest(value = 100, name = "Repetition {currentRepetition} of {totalRepetition}")
public void shouldReturnFizzIfDiv3() throws Exception {
assertEquals("Fizz", sut.fizzBuzz(3));
}
@RepeatedTest(value = 100, name = "Repetition {currentRepetition} of {totalRepetition}")
public void shouldReturnVal() throws Exception {
assertEquals("2", sut.fizzBuzz(2));
}
}
Po drugie, adnotacja ta ma parametr name, który pozwala na dodanie nazwy specyficznej dla uruchomienia. W nazwie tej możemy wykorzystać trzy flagi:
- {currentRepetition} – w której jest numer aktualnego przebiegu,
- {totalRepetition} – w której mamy ilość wszystkich przebiegów,
- {displayName} – która zawiera nazwę testu zdefiniowaną w @DisplayName.
Możemy też jako wartość parametru podać jedną ze stałych zdefiniowanych w samej adnotacji:
- SHORT\_DISPLAY\_NAME – która jest równoważna „repetition {currentRepetition} of {totalRepetition}”,
- LONG\_DISPLAY\_NAME – która jest równoważna „{displayName} :: repetition {currentRepetition} of {totalRepetition}”.
Problem polega jednak na tym, że nie możemy skonfigurować liczby wątków, które zostaną wykorzystane do uruchomienia testu. Nie pozostaje nic innego jak użycie specyficznego silnika, którego oczywiście jeszcze nie ma.