W moim obecnym projekcie trudno mi znaleźć dobre rozwiązanie do tworzenia skalowalnych testów integracyjnych, które nie mają skutków ubocznych. Trochę wyjaśnienia na temat właściwości wolnych od skutków ubocznych: chodzi głównie o bazę danych; po zakończeniu testów nie powinno być żadnych zmian w bazie danych (należy zachować stan). Może skalowalność i zachowanie stanu nie idą w parze, ale naprawdę chcę naciskać na lepsze rozwiązanie.
Oto typowy test integracji (testy dotykają warstwy bazy danych):
public class OrderTests {
List<Order> ordersToDelete = new ArrayList<Order>();
public testOrderCreation() {
Order order = new Order();
assertTrue(order.save());
orderToDelete.add(order);
}
public testOrderComparison() {
Order order = new Order();
Order order2 = new Order();
assertFalse(order.isEqual(order2);
orderToDelete.add(order);
orderToDelete.add(order2);
}
// More tests
public teardown() {
for(Order order : ordersToDelete)
order.delete();
}
}
Jak można sobie wyobrazić, takie podejście daje niezwykle powolne testy. Po zastosowaniu do całych testów integracyjnych testowanie tylko niewielkiej części systemu zajmuje około 5 sekund. Mogę sobie wyobrazić, że liczba ta wzrośnie, gdy zasięg wzrośnie.
Jakie byłoby inne podejście do pisania takich testów? Jedną z alternatyw, o której myślę, jest rodzaj zmiennych globalnych (w ramach klasy) i wszystkie metody testowe mają tę zmienną. W rezultacie powstaje i usuwanych jest tylko kilka zamówień; co skutkuje szybszymi testami. Myślę jednak, że wprowadza to większy problem; testy nie są już izolowane, a ich zrozumienie i analiza staje się coraz trudniejsza.
Może się zdarzyć, że testy integracyjne nie będą przeprowadzane tak często, jak testy jednostkowe; dlatego niska wydajność może być do zaakceptowania dla nich. W każdym razie dobrze byłoby wiedzieć, czy ktoś wymyślił alternatywy dla poprawy skalowalności.
Jest to odwieczny problem, z którym wszyscy spotykają się podczas pisania testów integracyjnych.
Idealnym rozwiązaniem, szczególnie jeśli testujesz na produkcji, jest otwarcie transakcji w konfiguracji i wycofanie jej po zakończeniu. Myślę, że powinno to pasować do twoich potrzeb.
Tam, gdzie nie jest to możliwe, na przykład gdy testujesz aplikację z poziomu warstwy klienta, innym rozwiązaniem jest użycie bazy danych na maszynie wirtualnej i zrobienie migawki w konfiguracji i powrót do niej po rozerwaniu (to nie tak długo, jak możesz się spodziewać).
źródło
Testy integracyjne należy zawsze przeprowadzać w odniesieniu do konfiguracji produkcji. W twoim przypadku oznacza to, że powinieneś mieć tę samą bazę danych i serwer aplikacji. Oczywiście ze względu na wydajność można zdecydować się na użycie DB w pamięci.
Nigdy nie powinieneś jednak rozszerzać zakresu transakcji. Niektóre osoby sugerowały przejęcie kontroli nad transakcją i wycofanie jej po testach. Gdy to zrobisz, wszystkie jednostki (zakładając, że używasz JPA) pozostaną przywiązane do kontekstu trwałości przez cały czas wykonywania testu. Może to spowodować bardzo nieprzyjemne błędy, które są bardzo trudne do znalezienia .
Zamiast tego należy wyczyścić bazę danych ręcznie po każdym teście przez bibliotekę taką jak JPAUnit lub podobną do tego podejścia (wyczyść wszystkie tabele za pomocą JDBC).
Ze względu na problemy z wydajnością nie należy wykonywać testów integracyjnych dla każdej kompilacji. Pozwól na to serwerowi ciągłej integracji. Jeśli korzystasz z Maven, możesz cieszyć się bezpieczną wtyczką, która pozwala rozdzielić testy na testy jednostkowe i integracyjne.
Nie powinieneś też kpić z niczego. Pamiętaj, że testujesz integrację, tzn. Testujesz zachowanie w środowisku wykonawczym.
źródło
Skalowalność
Już wcześniej miałem ten problem, że testy integracyjne trwały zbyt długo i nie były praktyczne, aby pojedynczy programista działał w ciasnej pętli zmian. Niektóre strategie radzenia sobie z tym są:
Spróbuj połączyć te techniki, aby uzyskać lepszy efekt.
źródło
Do celów testowych wykorzystaliśmy oparte na plikach wdrożenie bazy danych SQLite (wystarczy skopiować zasób). Dokonano tego, abyśmy mogli również przetestować migracje schematów. O ile mi wiadomo, zmiany schematu nie są transakcyjne, więc nie zostaną wycofane po anulowaniu transakcji. Również brak konieczności polegania na obsłudze transakcji w konfiguracji testowej pozwala poprawnie przetestować zachowanie transakcji z aplikacji.
źródło