«

»

wrz 11

Mock vs Stub. Czy warto je odróżniać?

Do napisania niniejszego wpisu zainspirowała mnie pewna dyskusja, którą prowadziłem z kolegą po fachu. Dyskusje wywołało niefortunnie(wg. mojego kolegi) użyte przez ze mnie słowo mock zamiast stub. Za nim przejdę do opisywania dyskusji szybko trochę teorii i nomenklatury.

W tym wpisie celowo pomijam techniki takie jak TDD czy BDD i pewnie tracę przez to wielu czytelników;). Pragnę skupić się na tym czym jest i było testowanie. Testy jednostkowe, służą do badania czy nasz kod działa tak, jak myślimy że działa. Jak, więc przetestować metody czy klasy, które korzystają z wielu zależności? Odpowiedzią jest izolacja. Czyli każdą z tych zależności testujemy osobno. Tylko jak, mamy przetestować zachowanie metody, która korzysta metod innych obiektów? Innymi słowy, jak zdobyć izolację? Tutaj z pomocą właśnie przychodzą nam między innymi tytułowe terminy stub oraz mock.

Stub – jest to obiekt, który zawiera minimalną implementacja interfejsu wymaganej zależności. Minimalna to znaczy jaka? A no taka, żeby nie wyrzucać wyjątku podczas testowania. Zobaczmy poniższy przykład.

Mamy tutaj klasę NumberValidator, która waliduje w pewien sposób liczby, tj. sprawdza czy są one większe lub równe 0 jeśli tak jest zwraca prawdę, lub fałsz w przeciwnym przypadku. Dodatkowo jeśli liczba nie jest prawidłowa wywoływana jest metoda ReportError obiektu IErrorReportService. Jak przetestować, więc taką logikę? Zacznijmy od przetestowania wyników, jakie metoda powinna zwracać. Do testów użyje nUnit. Napiszmy więc testy które, weryfikują zwracany wynik, po przekazaniu do metody liczby dodatniej lub równej 0.

Do konstruktora klasy, NumberValidator, przekazujemy Null ponieważ w przypadku liczby dodatniej ta zależność nie jest wykorzystywana możemy, więc ją zignorować. Wszystko, ok testy przechodzą. Jak przetestować teraz przypadek gdy przekażemy do testu liczbę mniejszą od zera? Jeśli przekażemy, do konstruktora klasy NumberValidator Null to dostaniemy wyjątek ponieważ w tym przypadku, metoda ReportError będzie wywołana na nieustawionej referencji. (BTW taka sytuacja powinna być też przetestowana). Należy, więc przekazać “jakąś” implementację zależności i tu właśnie przychodzi nam z pomocą stub. Uzupełnijmy więc, nasze testy.

Jak widać, stub to jak już wspominałem naiwna implementacja. Można również zauważyć, że nie bierze udziału w testowaniu jest przekazywana tylko po to, aby móc przetestować metodę Validate. Pisanie jednak, takich klas tylko po to, aby przetestować jedną metodę czy klasę jest kłopotliwe, szczególnie jeśli musimy stworzyć wiele takich naiwnych implementacji. Ten sam test możemy napisać z wykorzystaniem biblioteki moq, która ułatwia nam tworzenie takich obiektów.

Jak widać, dopisałem tylko jedną linijkę a efekt, jest taki sam.

Mock – jest trochę bardziej skomplikowanym bytem. Również zapewnia naiwną implementację zależności, ale po za tym pozwala na weryfikację np. ile razy dana metoda była wywołana i z jakimi parametrami czyli śledzi interakcję z interfejsem, który implementuje. Mock w odróżnieniu od stub bierze udział w procesie testowania. Ze względu na swoją złożoność takich obiektów zwykle nie piszę się od zera, korzysta się z takich rozwiązań jak np. Moq czy FakeItEasy dostarczających gotowe implementacje.

Wróćmy do przykładu. Czy przetestowaliśmy całą logikę, metody Validate? Otóż nie, powinniśmy również przetestować taki przypadek, że jeśli przekażemy do metody liczbę ujemną metoda ReportError zostanie wywołana dokładnie raz. Taki przypadek jest również częścią logiki tej metody, powinniśmy więc zastosować izolację, wykorzystamy tutaj właśnie mock.

Jak widać, mock różni się tym od stub, że bierze udział w procesie testowania.

Wracając, więc do meritum. Całą dyskusję wywołało to, że w momencie, gdy powinienem był nazwać obiekt stubem, nazwałem go mockiem z czym mój kolega nie mógł się pogodzić. Jego argumentem było to że, skoro nie testuje interakcji z zależnością, to obiekt imitujący tą zależność jest stubem a nie mockiem. Oczywiście miał rację. Tylko czy przez to moje testy będą lepsze lub gorsze? Moim zdaniem ważne jest, aby pisać testy, które sprawdzają logikę w metody w izolacji, co osiągamy korzystając z imitacji zależności. A nie zastanawiać się jakim właściwie bytem jest ta imitacja zależności mockiem, stubem a może fakem, spyem czy innym doublem. Po ciekawej dyskusji, udało mi się przekonać kolegę, że nie ma to “aż” takiego znaczenia. Test powinien testować logikę a programista nie powinien zbyt dużo myśleć o nomenklaturze.

4 Komentarze

Skip to comment form

  1. jan

    Fakt. Odroznianie nazwy nie wiele da. Ja zas specjalnie pisze ta wypowiedz zeby zwrocic uwage na pewien blad. Wg mnie nie wolno w testach uzywwc klasy random. Testy powinny byc powtarzalne, a w Twoim przypadku jesli ktos zmieni logike i wlaczy test ktory przejdzie (jesli nie zastosuje tdd) pomysli ze jest ok. Odda kod na produkcje, po czym moze sie okazac ze u innego uzytkownika testy beda konczyc sie niepowodzeniem.

  2. graf

    Zgadzam się, że Random nie powinien być używany w testach, ponieważ wprowadza on niedeterminizm testów. Może to prowadzić do takich sytuacji, że testy w pewnych przypadkach będą przechodziły a w innych nie.
    Na przykład gdybym zmienił logikę metody Validate tak:

    Test Valid_Should_Return_True_For_Positive_Number czasami mogłby przechodzić przez co moglibysmy nie wykryć błedu. Dlatego poprawie testy. Dzieki za zwrocenie uwagi.

  3. Adam

     

    Wydaje mi się ze rozróżnianie ma sens bo jednak jest różnica pomiędzy tymi dwoma słowami. W momencie gdy rozmawiam z kimś o testach i ktoś mi powie zrób mocka to już wiem, że testuje zachowanie, a gdy powie stuba to, że testuje stan.  Wydaje mi się, że wypowiedzenie tych słów kluczowych dla drugiego człowieka daje mu wyobrażenie o tym o czym mówimy i ułatwia komunikacje, a to ma największe znaczenie.

  4. graf

    Wg mnie zarówno testowanie stanu jak i zachowania służy temu żeby przetestować logikę metody. Skoro cel jest taki sam to czy ważna jest informacja czym faktycznie jest obiekt, który wykorzystujemy do osiągnięcia tego celu. Tym bardziej, że właśnie mocka można tak skonfigurować by był zachowywał się jak stub. Moim zdaniem nie, bo tak jak pisałem ta informacja nie sprawi że nasze testy będą lepsze.

Odpowiedz na „grafAnuluj pisanie odpowiedzi

Twój adres e-mail nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Możesz użyć tych znaczników i atrybutów HTMLa: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Current day month ye@r *