Dziś pobawimy się najprostszymi testami JUnit 5. Samo tworzenie i uruchamianie testów nie różni się w znaczący od wersji 4. Przynajmniej z punktu widzenia użytkownika. Tworzymy test, odpalamy mavena albo też naciskamy Alt+Shift+F10 Enter i gotowe. Jest jednak kilka drobnych, acz znaczących różnic, jeżeli chcemy skorzystać z czegoś więcej. Na przykład przygotować konfiguracje do testu.

Test referencyjny w JUnit 4

Poniżej test JUnit 4, który stanowi punkt odniesienia dla kodu pisanego z użyciem JUnit 5. Nie jest to najlepszy możliwy test, ale nie chciałem zaśmiecać go niepotrzebnymi elementami np. dodatkowymi asercjami.

Listing 1. Testy napisane z użyciem JUnit4

public class FizzBuzzJUnit4WithoutRunnersTest {

	private FizzBuzz sut;

	@BeforeClass
	public static void classSetup() {
		Logger.getLogger("JUnit 4").info("Started at " + LocalDateTime.now());
	}

	@AfterClass
	public static void classTeardown() {
		Logger.getLogger("JUnit 4").info("Finished at " + LocalDateTime.now());
	}

	@Before
	public void setup() {
		sut = new FizzBuzz();
	}

	@Test
	public void shouldReturnFizzBuzzIfDiv3And5() throws Exception {
		assertEquals("FizzBuzz", sut.fizzBuzz(15));
	}

	@Test
	public void shouldReturnBuzzIfDiv5() throws Exception {
		assertEquals("Buzz", sut.fizzBuzz(5));
	}

	@Test
	public void shouldReturnFizzIfDiv3() throws Exception {
		assertEquals("Fizz", sut.fizzBuzz(3));
	}

	@Test
	public void shouldReturnVal() throws Exception {
		assertEquals("2", sut.fizzBuzz(2));
	}
}

Mamy tu cztery metody testowe oraz metodę setup, która będzie wykonywać się przed każdym testem. Dodatkowo mamy dwie metody classSetup i classTeardown, które są uruchamiane przed utworzeniem instancji klasy testowej. Co dokładnie tu się dzieje, to nie jest istotne.

Zmiany w JUnit 5

JUnit 5 wprowadził kilka drobnych zmian w nazewnictwie adnotacji. Nowe adnotacje znacznie lepiej opisują, co się dzieje. Nasza klasa testowa po traktowaniu JUnit 5 będzie wyglądać w następujący sposób:

Listing 2. Testy napisane z użyciem JUnit 5

public class FizzBuzzJUnit5Test {

	private FizzBuzz sut;

	@BeforeAll
	static void classSetup() {
		Logger.getLogger("JUnit 4").info("Started at " + LocalDateTime.now());
	}

	@AfterAll
	static void classTeardown() {
		Logger.getLogger("JUnit 4").info("Finished at " + LocalDateTime.now());
	}

	@BeforeEach
	public void setup() {
		sut = new FizzBuzz();
	}

	@Test
	public void shouldReturnFizzBuzzIfDiv3And5() throws Exception {
		assertEquals("FizzBuzz", sut.fizzBuzz(15));
	}

	@Test
	public void shouldReturnBuzzIfDiv5() throws Exception {
		assertEquals("Buzz", sut.fizzBuzz(5));
	}

	@Test
	public void shouldReturnFizzIfDiv3() throws Exception {
		assertEquals("Fizz", sut.fizzBuzz(3));
	}

	@Test
	public void shouldReturnVal() throws Exception {
		assertEquals("2", sut.fizzBuzz(2));
	}
}

I tak oto adnotacja @Before została zastąpiona przez @BeforeEach, a @BeforeClass przez @BeforeAll. Podobnie ma się sprawa z adnotacjami @After\*

Oczywiście nie jest to jedyna zmiana.

Wyłączanie testów

W JUnit 4 wyłączenie testu polegało na dodaniu adnotacji @Ignore:

Listing 3. Przykład wyłączonego testu JUnit 4

@Test
@Ignore
public void shouldReturnVal() throws Exception {
	assertEquals("2", sut.fizzBuzz(2));
	assertEquals("8", sut.fizzBuzz(8));
	assertEquals("11", sut.fizzBuzz(11));
}

O ile w przypadku adnotacji @Before\*/@After\* można było domyślić się jaka jest intencja użytkownika, to już w przypadku adnotacji @Ignore, jest znacznie gorzej.

Test ignorowany, czyli co?

Kilka razy miałem w różnych zespołach rozmowę na ten temat. Czy jeżeli test jest ignorowany, to można go wywalić? Czy oznacza to, że można zignorować jego rezultaty? A może coś jeszcze innego? Zazwyczaj kończyło się wdrożeniem kolejnego, świetnego, i oczywistego schematu nazywania testów, który umożliwia ich filtrowanie. Żeby było zabawniej, schematy nazewnicze średnio działają z metodami, co prowadzi do tworzenia „kontenerów na testy ignorowane”. Klas, które zawierają testy, ale wszystkie są wyłączone… no właśnie, wyłączone.

Oczywiście intencja programisty, który użył tej adnotacji jest zupełnie inna. Chce on wyłączyć test. Z jakiegoś powodu test nie powinien być uruchamiany. Nie oznacza to, że będzie on pomijany. Po prostu w danym momencie chcemy nie uruchamiać danego testu. W tym celu możemy użyć adnotacji @Disable:

Listing 4. Przykład wyłączonego testu JUnit 5

@Test
@Disable
public void shouldReturnVal() throws Exception {
	assertEquals("2", sut.fizzBuzz(2));
	assertEquals("8", sut.fizzBuzz(8));
	assertEquals("11", sut.fizzBuzz(11));
}

Intencja jest teraz oczywista. Test został wyłączony.

Podsumowanie

JUnit 5 nie zmienia sposobu pisania testów w jakiś drastyczny sposób. Nie na tym podstawowym, prostym poziomie. Zmiany, które wprowadza, są subtelniejsze. Nowe nazewnictwo znacznie lepiej opisuje intencje stojące za poszczególnymi fragmentami kodu. Tym samym ułatwia nam czytanie testów i ich zrozumienie. W połączeniu z innymi mechanizmami, które omówię w kolejnych wpisach, daje nam nowe możliwości w tworzeniu kodu testowego.