Jako że @Afish striggerował mnie pewnym postem, postanowowiłem wyjaśnić swoją krytykę mocków, tj. czemu uważam że Mockito i podobne biblioteki(jak Moq w C#) są stosowane nadmiernie i nic ne wnoszą.
Teraz żeby być bardziej precyzyjnym, pisząc o mockach mam na myśli rzeczy typu:
ValueGenerator vgMock = Mockito.mock(ValueGenerator.class);
when(vgMock.getValue()).thenReturn(7)
Jest jeszcze Wiremock czyli narzędzie do mockowania http, ale to już będe nazywał po prostu Wiremockiem, podobnie, jak z bazą danych w pamięci (nie będę H2 określał mockiem w tym poście).
A teraz wyjaśnię czemu sądze że mocki są nadmiernie stosowane i co zamiast nich, oraz dlaczego często lepsza jest odwrócona piramida testów, tj. podstawą są testy integracyjne(mówię o tych z poziomu aplikacji), a nie jednostkowego (cokolwiek tą jednostką) jest:
- Możemy przetestować całość aplikacji, cześć tej aplikacji to infrastruktura. Dodatkowo niektóre funkcje sa bardzo CRUDOwe.
Założmy że mamy do czynienia ze sklepem internetowym w którym mamy możliwość dostarczenia komentarza do produktu jeśli kupiliśmy go wczesniej. Co w takim przypadku jest ważne? Żeby użytkownik mógł dodać komentarz tylko jako on, a nie ktoś inny/niezalogowany, żeby sprawdzić czy może dodac komentarz(iloczyn warunków logicznych - czy kupił produkt oraz czy nie dodał komentarza) a na koniec zapisać ten komentarz. Jak takie coś można przetestowac z Mockito? No nijak, bo najważniejsze to przetesowanie security i SQL, bo przecież on jest raczej bardziej złożony niż if z dwoma warunkami które mogliśmy zamockować z Mockito. Najlepiej uruchomić baze aplikacje z testowa bazą danych, wykonać odpowienie requesty i zrobić asercję np. na status HTTP/ stan bazy testowej. Tak czy owak należy testować takie rzeczy jak security, zapytania SQL itp. No można by teoretycznie obiekty dostępu do baz danych testować oddzielnie, tylko po co skoro już uruchamiamy test? Możemy przetestować aplikację a nie to czy mockito nam dobrze zamockowało dane. - Testy integracyjne nie betonują tak kodu. Problem z testami "niższego rzędu" jest taki że bardzo są blisko kodu, a więc każda mała zmiana powoduje że trzeba zmieniać też również testy. Jak ludzie testują na poziomie klas to jest tragedia komplentna, trochę lepiej jest jak mamy jakieś fasady ( ciekawa prezentacja o tym
- Biblioteki do mockowania sa często nie dokońca intuicyjne i często się można pomylić przez to w testach. Dodatkowo gdy testujemy bardzo nisko poziomowo mamy cały zarypany kod jakimiś inicjacjami mocków, i nie sądze żeby to powodowało że takie testy da się pisać/modyfikować szybko, ani nie dodają czytelności.
- Szybkość/koszt testów - mówi się że testy integracyjne są wolne. Otóż nie do końca. Jesli korzystamy z takiego TestCointaners i startujemy testy to sam start jest rzeczywiście powolniejszy, ale po starcie testy bedą się raczej wykonywac szybko. Za to zaoszczędzimy czas na tym że testy nie są tak zabetonowane. Dlatego jeśli dla danej funkcji programu mamy 5 przypadków testowych, testy integracyjne mają sens.
Dodatkowo, zamiast korzystać z mocków możemy robic testowe implementacje interfejsów. Możemy mieć interface CurrencyRatesProvider, implementacje produkcyjną jak jakiś RestCurrencyRatesProvider i jakieś faki. Takie rozwiązania są często bardziej czytelne i zwięzłe niż jakieś Mockito when then/thenReturn.
Co w przypadku integracji z innymi systemami? Na JVM jest taka biblioteka jak Wiremock która umożliwia postawienie stuba z zamockowanym api. Oczywiście jest to mock, ale mock o wiele bardziej inteligenty gdzie my wywołujemy zapytanie HTTP z poziomu testu i pewnie podobne są dostepne dla innych środowisk.
Kiedy tak naprawde sens testowania jednostkowego? W przypadku gdy chcemy wyodrębnić jakiś bardziej złożony algorytm który może mieć tez wiele różnych warunków, na przykład może to być kalkulator promocji z których część włącza się do innych, a część nie, a łącznie dla danego zamówienia aktywnych będzie na przykład 3 albo i 4 (jak dwie ksiązki w cenie jednej, promocja z okazji black friday, zniższka powyżej iluś tam złotych i jakiś kod rabatowy do tego).
Gdybym miał napisać TL;DR - po co tak naprawde stosowac mocki skoro i tak chcemy sprawdzić działanie aplikacji, w tym na przykład security, zapytania SQL, itp.