Hej, mam małe pytanka o testowanie integracyjne w Springu (pytania 1,2 w sumie ogólne):
- Czy testy integracyjne mogą współdzielić stan, czy po każdym powinienem czyściś/incijalizować DB ponownie? W sumie wydaje mi się, że szybciej byłoby raz wypełnić danymi niż przed każdym testem
Nie powinny, ponieważ testy integracyjne, to nadal testy, które obejmują jakąś specyficzną jednostkę systemu. Żeby miały sens, to powinny być powtarzalne, a to implikuje niezależność.
- Jaki jest najlepszy sposób dla wypełnienia DB przed testami - użyć normalnie usługi REST i wysłać żądania (np. utworzyć tak max kilka obiektów) czy jakoś inaczej wypełnić DB (np. adnotacja
@Sql
- https://docs.spring.io/spring-framework/reference/testing/annotations/integration-spring/annotation-sql.html)
Można np. użyć kombinacji testcontaines i liqiudbase. Za każdym razem otrzymasz świeżą bazę, w której będziesz miał tylko te dane, których potrzebujesz.
- Czy mam jakąś gwarancję, że kolejność wykonania moich testów będzie zawsze taka sama jeśli nie używam adnotacji
@Order
. Coś czytałem o jakiejś domyślnej kolejności, ale coś mi się nie zgadza jak patrzę na kolejność wykonania testów w Intellij (czy za pomocą przycisku w IDE, czy mvn clean install)
Nie masz gwarancji. Dobrze napisane testy nie powinny zależeć od siebie.
- Z tego co czytam, to domyślnie JUnit zakłada, że dla każdego testu instacja klasy jest tworzona ponownie. Ale jak rozumiem - nie dotyczy to automatycznego czyszczenia bazy (dla testów mam h2)? Po wykonaniu początkowych testów, które testują tworzenie - w kolejnych które testują odczyt np. listy obiektów widać te nowo utworzone w poprzednich testach
To raczej zły design testu. Nie dość, że uzależniasz jedne testy od drugich czasowo (kolejność wykonania), to jeszcze silnie wiążesz je stanem. Praktycznie nie masz możliwości wprowadzenia nowych funkcjonalności bez psucia całego kodu. Przy czym masz tutaj dość często spotykany błąd, który polega na rozdzieleniu funkcjonalności pod kątem konkretnej operacji, a nie scenariusza.
Przykład złego designu:
- Test 1: Zapisz ABC do bazy
- Test 2: Odczytaj zapisaną wartość ABC z bazy.
- Test 3: Usuń wartość ABC z bazy.
I teraz pytanie, czy Test 2
nie jest przez przypadek asercją dla Test 1
i Test 3
? Oraz kolejne, czy Test 1
nie jest przez przypadek założeniem dla Test 2
i Test 3
.
Przykład poprawnego designu:
- Test 1: Jeżeli baza jest pusta, to po zapisaniu wartości ABC, mogę ją odczytać.
- Test 2: Jeżeli baza jest pusta, to po zapisaniu wartości ABC, zakładam, że baza nie jest pusta i mogę usunąć ABC i baza znowu jest pusta.
Można to przetłumaczyć na pseudo JUnita:
@Test
void test1(){
Assumptions.assumeThat(repository.readAll()).isEmpty();
repository.save("ABC")
Assertions.assertThat(repository.readAll()).isNotEmpty().containsOnly("ABC"); // Zapis się udał i mogę odczytać to co trzeba
}
@Test
void test2(){
Assumptions.assumeThat(repository.readAll()).isEmpty();
repository.save("ABC")
Assumptions.assumeThat(repository.readAll()).isNotEmpty(); // Zapis się zapewne udał, ale nie sprawdzam co zostało zapisane
repository.delete("ABC")
Assertions.assertThat(repository.readAll()).isEmpty(); // Usunięcie się udało, bo baza jest pusta
}
Założenia, to bardzo niedoceniany element testów, który znacząco ułatwia życie.