W ostatnio miałem okazje uczestniczyć w konferencjiConfitura. Jedna z prezentacjiokazała się szczególnie ciekawa.
Opis prezentacji:
Autor prezentacji: Piotr Stawirej
Zaprezentuję, jak można uprościć aplikację i umożliwić jej testowanie poprzez analizę napotkanych przypadków izastosowanych rozwiązań. Skupimy się na niestandardowych przypadkach oraz często powtarzalnych sytuacjach, którewystępują podczas naszej pracy. Nauczymy się jak testować problematyczne klasy, integrację z bibliotekami,frameworkamii usługami zewnętrznymi, jak skutecznie używać zaślepek (fakes), co mockować, a czego nie, jak testować aplikacjewielowątkowe, jak przyspieszać testy integracyjne, jak testować kod zależny od czasu i działania bez widocznegoefektu,jak testować tolerancję na błędy oraz jak ponownie wykorzystać akceptacyjne testy jednostkowe jako testy integracyjnelub API. Omówimy, dlaczego warto stosować Property Based Testing dla Value Object’ów, jak testować obiekty mutowalne,zapis (persistence), czy testować logowanie, jak testować kod asynchroniczny. Poznamy przydatne heurystyki testowaniaoraz alternatywę dla testów wydajnościowych.
Poniżej przedstawiam kilka moim zdaniem szczególnie istotnych aspektów przedstawionych w prezentacji.
Fake to implementacja prostego obiektu, która udaje funkcjonalność rzeczywistego obiektu, ale w sposóbuproszczony. Fake są często tworzone ręcznie jako proste implementacje interfejsów lub klas, które na potrzeby testówmają zachowywać się podobnie do prawdziwych obiektów.
Mock to obiekt, który symuluje zachowanie innych obiektów w systemie, umożliwiając kontrolowanie i sprawdzanieinterakcji. Mocki są często tworzone przy użyciu bibliotek (np. Mockito) ipozwalają programistom na kontrolowanie, jak obiekty testowane współdziałają z innymi zależnościami.
Jednym z ciekawych problemów omawianych podczas niedawnej prezentacji był przypadek testowy, który restartował kontekstSpringa przed każdą metodą testową, co skutkowało znacznym wydłużeniem czasu wykonania testów. Aby temu zaradzić, wartoposzukać bardziej efektywnych sposobów przygotowania się do testów. W opisanym przykładzie zamiast restartu zastosowanometodę setup, która usuwała wszystkie wiersze z bazy danych, skracając czas wykonania z 27 sekund do kilkusetmilisekund. Ta optymalizacja znacząco poprawiła cały proces testowania.
Innym interesującym podejściem podkreślonym w prezentacji było stosowanie implementacji Fake jako alternatywy dlaprostych Mocków. Implementacje Fake mają przewagę ponownego wykorzystania w wielu testach. Przykładem tego podejścia byłprosterepozytorium oparte na mapie.
Niezwykle interesującym przypadkiem omówionym podczas prezentacji było wykorzystanie testów jednostkowych jako testów nawyższym poziomie. Proces ten polegał na wyodrębnieniu interfejsu z komponentu odpowiedzialnego zaprzypadki użycia (np.serwisu aplikacyjnego w kontekście Domain-Driven Design). Następnie napisanotest jednostkowy, który korzystał tylko zMocków lub implementacji Fake, aby sprawdzić funkcjonalność aplikacji. Ten sam test mógł zostać skonfigurowany tak, abykorzystał z prawdziwych implementacji, na przykład repozytorium osadzonego lub opartego na kontenerach testowych. Cowięcej, ten sam test mógł być rozszerzony o testowanie interfejsu API poprzez zastąpienie komponentu implementującegoprzypadki użyciaklientem API lub rozwiązaniem opartym naSelenium.
Czasami w testach można napotkać wyniki, które są trudne do obserwacji, co może prowadzić do niejasności lub błędnegoprzypuszczenia, że oczekiwane zachowanie nie zostało zaimplementowane. Jest to szczególnie trudne w przypadku podejściaTest-Driven Development (TDD). Aby temu zaradzić, warto jawnie uwzględnić oczekiwane zachowanie w teście, nawet jeślinie jest ono bezpośrednio obserwowalne.
Testowanie oprogramowania może być wyzwaniem, ale zastosowanie odpowiednich strategii może znacznie poprawić efektywnośći jakość procesu testowania. Optymalizacja czasu wykonania testów, wykorzystanie implementacji Fake, testowanie naróżnych poziomach abstrakcji i zapewnienie obserwowalności wyników to tylko kilka z wielu przydatnych strategii. Mającświadomość tych wyzwań i stosując odpowiednie metody, programiści mogą skutecznie radzić sobie z trudnościami związanymiz testowaniem i dostarczać lepsze oprogramowanie.