Niestety, bardzo często, wybór narzędzia do testów jednostkowych sprowadza się do słów: „Znam PHPSpec, piszmy specki” lub „Wszyscy piszą w xUnit, więc korzystajmy z PHPUnit”. Uważam, że dobrze jest mieć porównanie między narzędziami, dlatego na przestrzeni kilku wpisów postaram się zbadać, w których sytuacjach lepiej jest skorzystać z PHPSpec, a kiedy lepiej wybrać PHPUnit.

Charakterystyka testów jednostkowych

Testem jednostkowym nazywamy program, który w oparciu o pamięć operacyjną oraz procesor jest w stanie zweryfikować, czy konkretna jednostka kodu spełnia ustalone przez developera założenia. Kiedy mówimy o założeniach, to mamy na myśli to, czy stan obiektu oraz zachodzące wewnątrz jego metod procesy działają tak, jak byśmy tego oczekiwali.

Dodatkowo, testy jednostkowe mają nazwie jednostkę, która najczęściej sprowadza się do pojedynczej metody (bądź funkcji, jeżeli nie piszemy obiektowo). Nic nie stoi jednak na przeszkodzie, abyśmy wybrali znacznie większą jednostkę; jeżeli testowany przez nas proces jest na tyle rozległy, że publikujemy do niego klasę fasady – możemy w taki sposób skonstruować stuby, aby nie uwzględniały one zależności będących częścią tej dużej logiki. To, co jest jednak konieczne, to potrzeba tworzenia stubów do wszystkiego, co pozwala na komunikację ze światem zewnętrznym. Jeżeli tego nie zrobimy, to nasz test straci moc bycia testem jednostkowym.

Pamiętać należy również o tym, że różnego typu metody potrzebują nieco innego podejścia do testowania. Z jednej strony, w kodzie możemy mieć metodę, która wykonuje operacje na innych obiektach (mogą to być również metody niezwracające żadnej wartości). Z drugiej strony, możemy chcieć przetestować metodę, która służy generowaniu bardzo skomplikowanych wartości, np. szyfrowania danych, generowanie slugów itp. Obydwa typy metod testuje się w nieco inny sposób, co należy wziąć pod uwagę podczas wyboru narzędzia do tworzenia testów.

Testowanie jednostkowe a PHPSpec

PHPSpec jest narzędziem klasy SpecBDD, które (niestety) bardzo mylnie jest kojarzone z testami jednostkowymi. Z bardzo powierzchownej strony, pisząc specki – piszemy testy. Jest kilka znanych środowisku problemów, ale koniec końców – da się. Gdyby jednak przyjrzeć się nieco dokładniej całej sprawie, to zauważymy detale, które na pewnym poziomie decyzyjnym potrafią robić dużą różnicę.

Różnice w podejściach – PHPUnit vs PHPSpec

W domenie testowania aplikacji biznesowych mamy do czynienia z dwoma różniącymi się od siebie podejściami: BDD oraz xUnit. Pomimo tego, że obydwa podejścia służą do tworzenia testów, to korzystając z jednego lub drugiego podejścia możemy osiągnąć nieco inne cele.

Celem BDD (ang. Behaviour-Driven Development) jest opisanie wymagań. W ekosystemie PHP korzystamy głównie z dwóch narzędzi opartych o BDD. Do opisywania wymagań funkcjonalnych aplikacji korzystamy z Behata. Do opisu wymagań technicznych poszczególnych klas posłuży nam omawiany w tym wpisie PHPSpec. Zarówno Behat jak i PHPSpec udostępniają bardzo deskryptywne mechaniki, co sprawia, że testy pisane w duchu BDD będą o wiele bardziej czytelne dla osób mniej technicznych. Niestety, z powodu badania jedynie zachowań, bardzo często narzędzia klasy BDD (w tym PHPSpec) są okrojone z części klasycznych funkcjonalności związanych z testowaniem.

Celem podejścia xUnit jest weryfikacja poprawnego wykonania algorytmów i procesów w sposób niekoniecznie prosty do weryfikacji przez osoby nietechniczne. Najpopularniejszym frameworkiem do pisania testów w tej metodologii jest PHPUnit. Dzięki bardzo technicznemu podejściu do testów mamy np. o wiele większą swobodę w wyborze jednostki, możliwość korzystania z lepszych mechanizmów automatycznych (np. data providery), czy ustalanie zależności testów i lepsze sterowanie ich wykonaniem.

Brak wsparcia dla testowania właściwości klas

Pierwszym problemem, z którym spotkają się programiści przy pracy z PHPSpec, jest to, w jaki sposób można zweryfikować stan obiektu. Niestety, tego za pomocą tej biblioteki zrobić się nie da. I nie chodzi tu jednak o to, że twórca PHPSpec zapomniał o tej funkcjonalności.

Specki mają na celu sprawdzenie designu kodu, czyli tego, w jaki sposób nasze metody integrują się z innymi obiektami biorącymi udział w wykonaniu testowanej metody. Mówiąc bardziej obiektowo, powiemy, że weryfikowane są jedynie komunikaty przekazywane między testowanym obiektem a jego kolaborantami. W nomenklaturze programowania obiektoweo komunikatem nazywamy publiczne metody zdefiniowane w klasie.

Krótko podsumowując, jeżeli potrzebujemy przetestować właściwości klas, to najlepszym wyjściem będzie skorzystanie z biblioteki PHPUnit.

Problemy przy zmianie jednostki w PHPSpec

Standardową jednostką testową jest metoda. Aby mieć pełną kontrolę nad jej wykonaniem, odcinamy wszystkie zależności za pomocą stubów. Nic nie stoi jednak na przeszkodzie, abyśmy zrezygnowali z części stubów na rzecz tych prawdziwych zależności. Najważniejsze (w kontekście testów jednostkowych), abyśmy nie komunikowali się ze światem zewnętrznym.

W PHPSpec jesteśmy w stanie przetestować metodę w ten rozszerzony sposób, czyli bez stubowania. Musimy jednak mieć na uwadze fakt, że poziomie tej biblioteki nie mamy dostępu do kontenera zależności naszej aplikacji. Oznacza to, że im głębiej chcemy skorzystać z naszych prawdziwych zależności, tym bardziej skomplikowane wywołanie konstruktora nas czeka (tym więcej słowa kluczowego new).

Specki są kapryśne, ale to dobrze 🙂

PHPSpec należy do narzędzi bardzo kapryśnych. Przykładowo:

  • Każde wywołanie metody wewnątrz obiektu musi zostać zarejestrowane i obsłużone.
  • Dodatkowo, dla speca bardzo ważne są nazwy obiektów, z którymi współpracujemy; po tych nazwach rozróżnia on, czy stub/mock jest zależnością klasy wstrzykiwaną przez konstruktor, czy jest to zwykły obiekt zawieruszony gdzieś w łańcuchu stubów.
  • Kolejnym udziwnieniem jest konieczność rozpoczynania nazwy scenariusza od prefixu it_ lub its_.
  • PHPSpec wymaga, abyśmy stosowali odbicie lustrzane względem drzewka klas kodu produkcyjnego (jedna klasa – jeden plik specki).

Z jednej strony, tak, są to rzeczy nas ograniczające. Wszystkie tego typu ograniczenia powodują, że próbujemy robić rzeczy na okrętkę. Czasami wręcz, pojedyncze scenariusze potrafią być rozciągnięte na kilkaset linii kodu.

Z drugiej strony – właśnie o to chodzi. To, że specki wyglądają źle, że nie trzymamy się w fasonie, którego wymagają – oznacza, że zbytnio kombinujemy w kodzie, a nawet, że nasz kod robi zbyt dużo rzeczy naraz. Zwłaszcza, jeżeli faktycznie mamy scenariusze długie na 100-200 linijek. Czasami PHPSpec wręcz pokazuje nam, że to nie jego potrzebujemy (powyższy przykład z testowaniem stanu obiektu).

Czy w PHPSpec można pisać testy jednostkowe?

Gdybym został przyparty do muru, to odpowiedziałbym, że tak – w PHPSpec można pisać testy jednostkowe. Sam korzystam z niego właśnie w tym celu. W mojej ocenie PHPSpec najlepiej sprawdza się w warstwie aplikacyjnej i domenowej, za co bardzo go cenię. Nie oznacza to jednak, jest on narzędziem, którym można zastąpić starego, dobrego PHPUnita.

Moim zdaniem dobrze jest korzystać z PHPSpeca jako narzędzia podstawowego, ponieważ ostatecznie bardzo dobrze wpływa na design i szeroko pojętą obiektowość w naszym kodzie. Nie powinniśmy jednak zapominać o PHPUnit, ponieważ on z kolei daje nam o wiele większe spektrum zastosowań.

Profesjonalista, zajmujący się na co dzień aplikacjami biznesowymi w ekosystemie PHP. Jego pasją jest odkrywanie nowych konceptów programistycznych oraz wzorce architektoniczne. Uwielbia również pisać testy, gdyż jak sam uważa, dobry kod to przetestowany kod.

Comments are closed.