Na wstępie: fajnie by było, abyś wiedział mniej więcej co to jest Mock, Stub i Fake - nie będę tego jakoś szczególnie objaśniał bo idea tego wpisu jest inna niż wstęp do "zaślepek". Tutaj możesz poczytać o różnych zaślepkach na przykładzie PHPUnit.
Spis treści, a jak!
- Wstępniak
- Kiedy Mock?
- Przykład zaślepiania metody typu Query
- Zaślepienie metody Query za pomocą Mocka
- Zaślepienie metody Query za pomocą Stuba
- Zaślepienie metody Query za pomocą Fake
- Przemyślenia na temat zaślepiania metod typu Query
- Przykład zaślepiania metody typu Command
- Zaślepienie metody Command za pomocą Stuba
- Zaślepienie metody Command za pomocą Mocka
- Zaślepienie metody Command za pomocą Fake
- Przemyślenia na temat zaślepiania metod typu Command
- Końcowe przemyślenia
Dla przypomnienia, zaślepka (z ang. Test Double) to obiekt, który jest przekazywany do obiektu testowanego zamiast obiektu będącego rzeczywistą zależnością w kodzie produkcyjnym. Przykładem może być wstrzyknięcie zaślepki obiektu ProductRepository
(abstrakcji na persystentną kolekcję produktów) do jakieś usługi, np. ProductService
.
Aby utworzyć obiekt ProductService
potrzeba ProductRepository
, aby utworzyć ProductRepository
musimy utworzyć EntityManagera
(zakładam implementację opartą na Doctrine), aby utworzyć EntityManagera
trzeba utworzyć konfigurację oraz połączenie z bazą danych, aby połaczyć się z bazą danych musimy mieć z czym się połaczyć, więc serwer bazy danych musi być zainstalowany, schema bazy powinno być utworzone itp. Aby utworzyć obiekt konfiguracji, trzeba utworzyć obiekt cache... Na litość Boską, ja tylko chcę przetestować czy ProductService::createProduct($name, $price)
tworzy obiekt produktu, zapisuje go w repozytorium i triggeruje przy tym odpowiedni event...
Powyższa historyjka pokazuje jedną z przyczyn stosowania zaślepek. Jest ich więcej, np:
- Uproszczenie setUp testu
- Testy w izolacji (tzw. testy jednostkowe), nie chcemy drugi raz testować
DoctrineProductRepository
- ta klasa ma osobne testy!
- Zwiększenie szybkości wykonywania testów - zewnętrzny web service zastępujemy lokalnym Stubem/Fake, repozytorium operujące na bazie zastępujemy implementacją in memory
- Możemy w prosty sposób przetestować sytuacje brzegowe, które ciężko zreprodukować wykorzystując rzeczywisty obiekt
- Podczas pisania implementacji klasy X, która zależy od typu Y, nie mamy jeszcze implementacji Y
Oczywiście są sytuacje, w których nie należy stosować zaślepek. Należy pamiętać, że nie należy zamieniać części obiektu/podsystemu który testujemy. Np. nie powinno się zastępować EntityManagera
implementacją "in memory" w testach DoctrineProductRepository
, gdyż testy te mają testować, czy poprawnie szukamy / zapisujemy dane w bazie.
W idealnym świecie, zgodnie z zasadą Command-Query separation, każda metoda powinna być typu Command lub Query.
- Command - zmienia stan obiektu/systemu nie zwracając żadnej wartości - najprostszy przykład to setter ;)
- Query - pobranie stanu obiektu/systemu nie zmieniając stanu - najprostszy przykład to getter
W złym guście jest tworzenie metod które zarówno są Command i Query, bo taka funkcja robi de facto dwie rzeczy - zmienia stan i go zwraca. Nie można wywołać 2x taką metodę aby pobrać stan, gdyż za każdym wywołaniem ten stan jest zmieniany.
Związek typu metod z zaślepkami:
- Mocki można stosować do zaślepiania metod typu Command, ale nie powinno się stosować do zaślepiania metod typu Query (są wyjątki)
- Stuby można stosować do zaślepiania metod typu Query, ale nie należy stosować do zaślepiania metod typu Command
- Fake można stosować do zaślepiania metod typu Query i Command
Krótkie wyjaśnienie aby nie było zamętu:
Czytaj dalej tutaj (rozwija treść wpisu)
Czytaj dalej na blogu autora...

Zwiń
Czytaj na blogu autora...