Testy jednostkowe FIRST - okładka

Testy jednostkowe FIRST

Opublikowano Kategorie Czysty kodCzas czytania 5min

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. Możemy go 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 lub z wykorzystaniem metody opisanej w tym artykule.

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 repozytorió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żdym 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 testó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 np. 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 zdecydowanie szybciej 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żdym środowisku. Zarówno na lokalnym, jak i na serwerze ciągłej integracji, ale i na każdym innym środowisku na którym 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 wspomnianych 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 powinien 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 pierwotnie 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, wedł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

Dominik Szczepaniak

Zawodowo Senior Software Engineer w CKSource. Prywatnie bloger, fan włoskiej kuchni, miłośnik jazdy na rowerze i treningu siłowego.

Inne wpisy, które mogą Cię zainteresować

Zapisz się na mailing i odbierz e-booka

Zapisując się na mój mailing, otrzymasz darmowy egzemplarz e-booka 106 Pytań Rekrutacyjnych Junior JavaScript Developer! Będziesz też otrzymywać wartościowe treści i powiadomienia o nowych wpisach na skrzynkę e-mail.

Subscribe
Powiadom o
guest

0 komentarzy
Inline Feedbacks
View all comments