Testy jednostkowe FIRST - okładka

Testy jednostkowe FIRST

Tematowi testów jednostkowych poświęciłem kilka artykułów na swoim blogu:

Jeśli jeszcze się z nimi nie zapoznałeś/aś to serdecznie zachęcam.

Tym razem poruszę temat zasad, którymi warto się kierować w trakcie pisania testów jednostkowych. Zasady te zostały zebrane pod akronimem FIRST.

Owy skrótowiec możemy rozszyfrować jako:

  • Fast
  • Isolated
  • Repeatable
  • Self-validating
  • Timely/Thorough

Testy jednostkowe FIRST - zasada Fast

Fast

Testy jednostkowe powinny być szybkie. Testy jednostkowe służą do testowania najmniejszych elementów systemu, zwykle metod. Takich testów w systemie powinno być (i zwykle jest) najwięcej. Liczba takich testów może rosnąć w lawinowym tempie (tysiące testów jednostkowych w systemie to nie jest nic niespotykanego). Co więcej, unit testy są uruchamiane najczęściej, przez co czas ich wykonania powinien być jak najkrótszy.

Aby zoptymalizować czas wykonywania testów jednostkowych, możemy zrobić kilka rzeczy. Przede wszystkim, należy unikać w testach jednostkowych tzw. “czekania”. Jeżeli nasz kod wykorzystuje metody bazujące na czasie tj. timeouty, interwały, sleepy, itd. to czas systemowy powinien być zmockowany. W JavaScript może to być osiągnięte przy pomocy biblioteki sinon:

const sinon = require( 'sinon' );

const clock = sinon.useFakeTimers(); // Zmockowanie zegara systemowego.

await clock.tickAsync( 25000 );

clock.restore(); // Przywrócenie oryginalnego zegara systemowego.

Powyższy przykład jest odpowiedzią na kod bazujący na np. setTimeout() czy setInterval().

Testy repozytoriów

Kolejną kwestią wartą rozważenia są testy repozytioriów i integracji z bazami danych. Być może lepszym miejscem do testowania repozytoriów i operacji na bazach danych będą testy integracyjne. Przede wszystkim, operacje na bazie danych mogą trwać. Warto tutaj pamiętać, że taki test to nie tylko dodanie i odczyt z bazy danych. Aby test dał jednoznaczny wynik, za każym razem infrastruktura bazodanowa powinna zostać postawiona od zera. Następnie należy uzyskać połączenie z bazą danych i dopiero wtedy można wykonać operacje na bazie danych. Co więcej, w przypadku takich testów korzystamy z oryginalnej bazy a nie z mocka, co całkowicie burzy ideę testu jednostkowego.

Isolated

Zgodnie z FIRST, testy jednostkowe powinny być izolowane. Oznacza to, że nigdy, ale to przenigdy, żaden test jednostkowy nie powinien zależeć od innego. Niedopuszczalna jest sytuacja, gdy do pomyślnego przejścia testu jednostkowego A potrzebne jest uruchomienie testu jednostkowego B. Oznacza to również, że testy nie mogą się wzajemnie psuć.

Dobrym sposobem na sprawdzenie izolacji tetsów jest ich współbieżne uruchomienie. W pełni poprawnie izolowane testy jednostkowe, uruchomione równocześnie, powinny dać identyczne rezultaty jak podczas uruchomienia sekwencyjnego.

W JavaScript, pomocny przy weryfikacji izolacji testów jednostkowych może okazać test runner o nazwie AVA. Cechą odróżniającą AVĘ od chociażby wciąż popularnego test runnera Mocha jest współbieżność uruchamiania testów.

Dzięki współbieżności zyskujemy nie tylko pewność izolacji ale też spełniamy zasadę Fast! Testy uruchomione współbieżnie są wykonane zdecydownie szyciej niż testy uruchomione szeregowo!

Repeatable

Testy zgodne z FIRST powinny być powtarzalne. Oznacza to, że test powinien dać identyczny rezultat za pierwszym razem jak i po tysiącu czy milionie uruchomień. Testy spełniający tę zasadę powinny dać taki sam rezultat na każym środowisku. Zarówno na lokalnym, jak i na serwerze ciągłej integracji, ale i na każdym innym środowisku na któym zdecydujemy się je uruchomić.

Test jednostkowy nie powinien zależeć w żaden sposób od zewnętrznych mechanizmów czy systemów, na przykład wcześniej wsspomnianych zegarów systemowych lub baz danych. Z pomocą tu przychodzą mocki.

Self-validation

Test jednostkowy musi dawać jednoznaczną odpowiedź czy przeszedł pozytywnie czy też nie. Test absolutnie nie powinien dawać pola do interpretacji jego wyniku. Oznacza to również, że nie powinna mieć miejsca manualna interpretacja rezultatu. Do uzyskania tego efektu służą asercje. Dzięki asercjom, test jest w stanie zwrócić jednoznaczną odpowiedź czy pewne zdarzenia zaszły w systemie, lub czy dana wartość jest wartością oczekiwaną.

Co więcej, test pownien sprawdzać tylko tyle ile jest absolutnie konieczne do przejścia testu. Jeżeli w jednym teście sprawdzamy, czy wewnątrz danej metody została wywołana inna metoda oraz czy pierwonie testowana metoda zwraca konkretny wynik, to test należy rozłożyć na dwa osobne przypadki testowe.

Testy jednostkowe FIRST - zasada Timely

Timely/Thorough

Zgodnie z zasadami FIRST, test powinien być możliwy do napisania w dowolnym momencie czasu. Zasada ta, wdług mojej interpretacji, nie determinuje kiedy konkretnie należy pisać testy. Szczególnie użyteczne jest to dla fanów podejścia Test Driven Development. Jeśli zaś wolisz pisać testy po napisaniu kodu produkcyjnego to również nic nie powinno stać na przeszkodzie.

Także, według tej zasady powinniśmy myśleć o testach w trakcie pisania kodu produkcyjnego i vice versa.

Źródła i materiały dodatkowe: