Piszę przypadki testowe jUnit dla 3 celów:
- Aby upewnić się, że mój kod spełnia wszystkie wymagane funkcje, we wszystkich (lub w większości) kombinacjach / wartościach wejściowych.
- Aby upewnić się, że mogę zmienić implementację i polegać na testach JUnit, aby powiedzieć, że cała moja funkcjonalność jest nadal zadowolona.
- Jako dokumentacja wszystkich przypadków użycia mój kod obsługuje i działa jako specyfikacja do refaktoryzacji - na wypadek, gdyby kod kiedykolwiek musiał zostać przepisany. (Zmodyfikuj kod, a jeśli moje testy jUnit zakończą się niepowodzeniem - prawdopodobnie przegapiłeś jakiś przypadek użycia).
Nie rozumiem, dlaczego i kiedy Mockito.verify()
należy go użyć. Kiedy widzę verify()
, że ktoś mnie wzywa, mówi mi, że mój JUnit zaczyna być świadomy implementacji. (W ten sposób zmiana mojej implementacji spowodowałaby uszkodzenie moich JUnits, mimo że moja funkcjonalność nie uległa zmianie).
Szukam:
Jakie powinny być wytyczne dotyczące właściwego użytkowania
Mockito.verify()
?Czy zasadniczo poprawne jest, aby jUnits był świadomy implementacji testowanej klasy lub był ściśle z nią związany?
java
unit-testing
junit
mockito
Russell
źródło
źródło
Odpowiedzi:
Jeśli umowa klasy A obejmuje fakt, że wywołuje metodę B obiektu typu C, należy to przetestować, wykonując próbkę typu C i sprawdzając, czy metoda B została wywołana.
Oznacza to, że umowa klasy A ma wystarczającą szczegółowość, że mówi o typie C (który może być interfejsem lub klasą). Tak, mówimy o poziomie specyfikacji, który wykracza poza „wymagania systemowe” i w pewien sposób opisuje sposób implementacji.
Jest to normalne w przypadku testów jednostkowych. Podczas testów jednostkowych chcesz mieć pewność, że każda jednostka robi „właściwą rzecz”, a to zwykle obejmuje interakcje z innymi jednostkami. „Jednostki” mogą tutaj oznaczać klasy lub większe podzbiory aplikacji.
Aktualizacja:
Wydaje mi się, że nie dotyczy to tylko weryfikacji, ale także stubowania. Gdy tylko wprowadzisz metodę klasy współpracującej, test jednostkowy w pewnym sensie zależy od implementacji. Tak jest w rodzaju testów jednostkowych. Ponieważ w Mockito chodzi zarówno o stubowanie, jak i o weryfikację, fakt, że używasz Mockito w ogóle oznacza, że będziesz miał do czynienia z tego rodzaju zależnością.
Z mojego doświadczenia wynika, że jeśli zmieniam implementację klasy, często muszę zmieniać implementację testów jednostkowych, aby ją dopasować. Zazwyczaj, choć nie będę musiał zmienić wykaz co jednostka testy nie są dla klasy; chyba że powodem zmiany było istnienie warunku, którego wcześniej nie testowałem.
Właśnie o to chodzi w testach jednostkowych. Test, który nie cierpi z powodu tego rodzaju zależności od sposobu wykorzystania klas współpracowników, jest tak naprawdę testem podsystemu lub testem integracji. Oczywiście są one często pisane przy użyciu JUnit i często wymagają użycia szyderstwa. Moim zdaniem „JUnit” to okropna nazwa dla produktu, który pozwala nam produkować różnego rodzaju testy.
źródło
equals()
lubequalsIgnoreCase()
nigdy nie byłoby czymś określonym w wymaganiach klasy, więc nigdy nie miałby testu jednostkowego per se. Jednak „zamknięcie połączenia DB po zakończeniu” (cokolwiek to oznacza pod względem implementacji) może być wymogiem klasy, nawet jeśli nie jest to „wymaganie biznesowe”. Dla mnie sprowadza się to do relacji między umową ...Odpowiedź Davida jest oczywiście poprawna, ale nie wyjaśnia, dlaczego tak chcesz.
Zasadniczo podczas testowania jednostek testujesz jednostkę funkcjonalności w izolacji. Testujesz, czy dane wejściowe dają oczekiwane wyniki. Czasami musisz również przetestować działania niepożądane. Krótko mówiąc, weryfikacja pozwala to zrobić.
Na przykład masz trochę logiki biznesowej, która ma przechowywać rzeczy za pomocą DAO. Można to zrobić za pomocą testu integracji, który tworzy instancję DAO, łączy ją z logiką biznesową, a następnie przeszukuje bazę danych, aby sprawdzić, czy oczekiwane rzeczy zostały zapisane. To już nie jest test jednostkowy.
Możesz też kpić z DAO i sprawdzić, czy zostanie wywołany w oczekiwany sposób. Za pomocą mockito możesz sprawdzić, czy coś jest wywoływane, jak często jest ono wywoływane, a nawet używać dopasowań parametrów, aby upewnić się, że jest wywoływane w określony sposób.
Drugą stroną takich testów jednostkowych jest to, że wiążesz testy z implementacją, co utrudnia refaktoryzację. Z drugiej strony, dobry zapach projektu to ilość kodu potrzebna do jego prawidłowego wykonania. Jeśli twoje testy muszą być bardzo długie, prawdopodobnie coś jest nie tak z projektem. Więc kod z wieloma efektami ubocznymi / złożonymi interakcjami, które należy przetestować, prawdopodobnie nie jest dobrą rzeczą.
źródło
To świetne pytanie! Myślę, że podstawowa przyczyna tego jest następująca: używamy JUnit nie tylko do testowania jednostkowego. Pytanie powinno zostać podzielone:
więc jeśli zignorujemy testy wyższe niż jednostkowe, pytanie można sformułować ponownie: „ Używanie białych testów jednostkowych za pomocą Mockito.verify () tworzy świetną parę między testem jednostkowym a moją możliwą implementacją, czy mogę zrobić trochę „ szarej skrzynki „ testowanie jednostkowe i jakie podstawowe zasady powinienem zastosować do tego ”.
Teraz przejdźmy przez cały ten krok po kroku.
* - Czy powinienem używać Mockito.verify () w mojej integracji (lub innych testach wyższych niż jednostkowe)? * Myślę, że odpowiedź brzmi zdecydowanie nie, poza tym nie powinieneś używać do tego mocków. Twój test powinien być jak najbardziej zbliżony do rzeczywistej aplikacji. Testujesz pełny przypadek użycia, a nie wydzieloną część aplikacji.
* Testowanie jednostkowe czarnej skrzynki a białej skrzynki * Jeśli używasz metody czarnej skrzynki do tego, co naprawdę robisz, podajesz (wszystkie klasy równoważności) dane wejściowe, stan i testy, które otrzymasz oczekiwany wynik. W tym podejściu stosowanie mocków w ogóle jest uzasadnione (po prostu naśladujesz, że robią to dobrze; nie chcesz ich testować), ale wywołanie Mockito.verify () jest zbyteczne.
Jeśli stosujesz podejście „ białej skrzynki”, co naprawdę robisz, testujesz zachowanie swojego urządzenia. W tym podejściu niezbędne jest wywołanie Mockito.verify (), powinieneś sprawdzić, czy twoje urządzenie zachowuje się tak, jak się tego spodziewasz.
podstawowe zasady testowania szarych skrzynek Problem z testami szarych skrzynek polega na tym, że tworzy ono wysokie sprzęganie. Jednym z możliwych rozwiązań jest przeprowadzenie testu szarej skrzynki, a nie testowanie białej skrzynki. Jest to swego rodzaju połączenie testów czarnych i białych skrzynek. Naprawdę testujesz zachowanie swojego urządzenia, tak jak w przypadku testów białych skrzynek, ale ogólnie czynisz go agnostycznym, jeśli to możliwe . Gdy jest to możliwe, po prostu sprawdzisz, jak w przypadku czarnej skrzynki, po prostu zapewnisz, że wynik jest tym, czego się spodziewasz. Istota twojego pytania brzmi: kiedy jest to możliwe.
To jest naprawdę trudne. Nie mam dobrego przykładu, ale mogę podać przykłady. W przypadku, o którym wspomniano powyżej za pomocą equals () vs equalsIgnoreCase (), nie powinieneś wywoływać Mockito.verify (), po prostu sprawdź dane wyjściowe. Jeśli nie możesz tego zrobić, podziel kod na mniejsze jednostki, dopóki nie będziesz w stanie tego zrobić. Z drugiej strony, załóżmy, że masz trochę @Service i piszesz @ Web-Service, który jest zasadniczo opakowany w twoją @Service - deleguje wszystkie wywołania do @Service (i robi dodatkową obsługę błędów). W takim przypadku wywołanie Mockito.verify () jest niezbędne, nie powinieneś powielać wszystkich swoich kontroli wykonanych dla @Serive, wystarczające jest sprawdzenie, czy dzwonisz do @Service z poprawną listą parametrów.
źródło
Muszę powiedzieć, że masz absolutną rację z punktu widzenia klasycznego podejścia:
Należy pamiętać, że nie ma uniwersalnych narzędzi. Rodzaj oprogramowania, jego wielkość, cele firmy i sytuacja rynkowa, umiejętności zespołu i wiele innych rzeczy wpływają na decyzję, które podejście zastosować w konkretnym przypadku.
źródło
Jak powiedzieli niektórzy ludzie
Jeśli chodzi o obawy związane z przełamaniem testów podczas refaktoryzacji, jest to nieco oczekiwane w przypadku korzystania z próbnych / odcinków / szpiegów. Mam na myśli to z definicji, a nie konkretnej implementacji, takiej jak Mockito. Ale można myśleć w ten sposób - jeśli trzeba zrobić refaktoryzacji która powodowałaby znaczne zmiany na sposób działania metody, to jest dobry pomysł, aby zrobić to na podejściu TDD, dzięki czemu można zmienić testu najpierw do zdefiniowania nowe zachowanie (które zakończy się niepowodzeniem testu), a następnie dokonaj zmian i ponownie uzyskaj pozytywny wynik testu.
źródło
W większości przypadków, gdy ludzie nie lubią korzystać z Mockito.verify, dzieje się tak, ponieważ służy on do weryfikacji wszystkiego, co robi testowana jednostka, a to oznacza, że będziesz musiał dostosować test, jeśli coś się w nim zmieni. Ale nie sądzę, że to jest problem. Jeśli chcesz mieć możliwość zmiany działania metody bez konieczności zmiany jej testu, oznacza to w zasadzie, że chcesz pisać testy, które nie sprawdzają wszystkiego, co robi twoja metoda, ponieważ nie chcesz, aby testowała twoje zmiany . I to jest zły sposób myślenia.
Naprawdę problemem jest to, czy możesz zmodyfikować to, co robi twoja metoda, a test jednostkowy, który ma całkowicie obejmować tę funkcję, nie zawiedzie. Oznaczałoby to, że bez względu na cel zmiany, wynik tej zmiany nie jest objęty testem.
Z tego powodu wolę wyśmiewać jak najwięcej: wyśmiewaj także swoje obiekty danych. Wykonując tę czynność, możesz nie tylko użyć polecenia Sprawdź, aby sprawdzić, czy wywoływane są prawidłowe metody innych klas, ale także czy przekazywane dane są gromadzone za pomocą prawidłowych metod tych obiektów danych. Aby to zakończyć, należy przetestować kolejność wykonywania połączeń. Przykład: jeśli zmodyfikujesz obiekt encji db, a następnie zapiszesz go za pomocą repozytorium, nie wystarczy zweryfikować, czy seteri obiektu są wywoływane z poprawnymi danymi i czy wywoływana jest metoda zapisu repozytorium. Jeśli są wywoływane w niewłaściwej kolejności, twoja metoda nadal nie robi tego, co powinna. Tak więc nie używam Mockito.verify, ale tworzę obiekt inOrder ze wszystkimi próbami i zamiast tego używam inOrder.verify. A jeśli chcesz to zrobić, powinieneś również zadzwonić do Mockito. ZweryfikujNoMoreInteractions na końcu i przekaż mu wszystkie symulacje. W przeciwnym razie ktoś może dodać nową funkcjonalność / zachowanie bez testowania jej, co oznacza, że po tym czasie statystyki pokrycia mogą wynosić 100%, a Ty nadal gromadzisz kod, który nie jest potwierdzony ani zweryfikowany.
źródło