Już dostępny

Program Szkoleniowy Java Developer dostępny 🔥💪 tylko TERAZ za 1299 zł  Sprawdź szczegóły i agendę

Zakres

Monitoring • Apache Kafka • Clean Code Testowanie • Hibernate • Systemy kolejkowe Sprawdź szczegóły i agendę

Zakres

14 modułów  /  ponad 40h nagrań  /  230 lekcji  /  dożywotni dostęp  /  Sprawdź szczegóły i agendę

Zasady pisania dobrych testów – F.I.R.S.T. i konkretne techniki żeby je zastosować

utworzone przez 9 października 2021Java, Testowanie

[Szybkie info]: Startujemy z IV edycją Programu Szkoleniowego Java Developera 🚀. To MEGA piguła wiedzy o Java 🔥💪

  • 14 tygodniowy program szkoleniowy online,
  • 230 lekcji w formie video (40 godzin materiału)
  • z dożywotnim dostępem
  • Case Studies, masz dostęp do kodu i obrazów Dockerowych
  • zamknięta grupa mentorzy + uczestnicy i webinary na żywo

W agendzie znajdziesz: Mikroserwisy, Systemy kolejnowe, Apache Kafka, Caching, Hibernate/MyBatis/Spring Data, techniki efektywnych Testów kodu, Clean Code i Maven.

Tylko teraz dołączysz z 50% rabatem to 2699 zł 1299 zł (+VAT). I nigdy już nie będzie taniej. Poniżej dowiesz się więcej:

Zobacz więcej

A teraz przechodzimy do artykułu:

Istnieją ogólnie przyjęte zasady, które warto stosować podczas pisania testów. Sprawiają, że testy są bardziej użyteczne w Twoim projekcie. Do każdej z tych zasad podam Ci konkretne techniki i wskazówki, aby je spełnić.

Zasady mają akronim F.I.R.S.T. i kolejne litery oznaczają:

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

Nie chcę, żeby zabrzmiało to jak oklepany temat z rozmów rekrutacyjnych, ale chcę Ci pokazać, co KONKRETNIE możesz zrobić, żeby JUŻ TERAZ poprawić użyteczność testów w Twoim projekcie.

(F) Fast

Uruchomienie testów powinno być szybkie. Sprawia to, że otrzymujemy szybką informację zwrotną, czy wprowadzona zmiana wprowadziła błąd, czy nie. Szybkie testy nie zniechęcają ludzi do ich uruchamiania, tworzenia nowych i utrzymywania.

Wyobraź sobie, że aby uruchomić jakikolwiek test lub zestaw musisz poczekać 20 sekund. Podczas tych 20 sekund jest wysoce prawdopodobne że ulegniesz rozproszeniu, albo będzie silna pokusa „zrobienia czegoś w międzyczasie”. Zerkniesz gdzieś, odpiszesz na maila. Ulegniesz rozproszeniu, a Twoja produktywność spadnie przez tzw. context switching, czyli stracenie/zmianę kontekstu wykonywanej czynności. Pamięć krótkotrwała zostanie wyczyszczona i trzeba poświęcić dodatkową energię na zorientowanie się, co właściwie przed chwilą robiłem, na czym stanęło. Spada produktywność. Jesteś kognitywnie przeciążony. Twoja praca staje się mniej efektywna.

Przyczyną długo wykonujących się testów są głównie:

  • Interakcje I/O
  • Opóźnienia (timeouty)
  • Tworzenie i odświeżanie kontekstu Spring

Jak to zoptymalizować?

Niektóre testy siłą rzeczy wykonują się dłużej. Są to głównie testy integracyjne, które potrzebują interakcji z I/O. Tam, gdzie tylko jest to możliwe izoluj I/O. Wykorzystuj implementacje in-memory lub całkowicie zaślepiaj takie interakcje.

Bądź pragmatyczny. W testach unitowych stosuj całkowitą izolacją I/O, a implementacje sprawdzaj w testach integracyjnych. Zastosuj piramidę testów. Wykonuj zestaw szybkich testów najpierw, później wolniejszych, tak, aby dostać jak najszybszy feedback.

Z implementacji in-memory (które też bywają wolne) korzystaj w testach komponentowych (nie unitowych).

Twój małe konteksty Spring-owe, jeżeli unitem jest pakiet, a nie unit. Staraj się nie modyfikować klas w kontekście Spring-a, który wymaga potem przeładowania (tzw. dirty context).

Gdy testujesz funkcjonalność z opóźnieniem zegara, zamiast wstawienia sleep() w testach skorzystaj z zamockowanego zegara, który możesz zmodyfikować:

Clock baseClock = Clock.fixed(Instant.now(), ZoneId.systemDefault());
// do action

Clock after = Clock.offset(baseClock, Duration.ofSeconds(5));
// check operation

Psst… Interesujący artykuł?

Jeżeli podoba Ci się ten artykuł i chcesz takich więcej – dołącz do newslettera. Nie ominą Cię materiały tego typu.

.

(I) Isolated, Independent

Testy nie powinny zależeć od siebie. Pozwala to na uruchamianie ich osobno i w całym zestawie. Zawsze dają takie same wyniki.

Testy zależą od siebie, kiedy po wykonaniu testu ustawiany jest jakiś efekt uboczny we współdzielonych obiektach, np. mockach, danych, stanów obiektów. Wtedy kolejne ich uruchamianie nie będzie deterministyczne.

Dlatego jeżeli test oczekuje jakiegoś stanu, powinien sobie go utworzyć w izolacji, tylko na czas działania. Jeżeli to możliwe – twórz obiekty lokalnie w fazie given.

Jeżeli istnieją wspólne implementacje, bo koszt utworzenia nowych obiektów jest zbyt wysoki, wtedy przywróćmy stan zmodyfikowanych obiektów (posprządanie po teście). Przykłady:

  • Współdzielone Mocki w kontekście Spring-a po wykonaniu testu resetują się (dzięki runnerom).
  • Stan w implementacjach in-memory powinien zostać zresetowany.

Gdy zadbasz o tę higienę, testy nie będą zależały od siebie.

(R) Repeatable

Płynnie przechodzimy do kolejnej zasady. Testy powinny być powtarzalne w każdych warunkach.

Powinny zależeć tylko i wyłącznie od kodu, który testują. Nie powinny zależeć od czynników zewnętrznych, na przykład poprzednich testów ale też od dostępności bazy danych, sieci, plików.

Jeżeli zależą od czegoś jeszcze, to nie dają klarownego wyniku PASS lub FAIL. Raz test przechodzi, raz nie. Nazywa się to kruchym testem, (flaky test), łatwo go uszkodzić.

Istnieje klasa testów integracyjnych, która co do zasady ma małą izolację. W porządku, dajmy takie testy do osobnej kategorii, wykonywanych później lub w osobnym zadaniu w Contineous Integration. Możemy wtedy sami ocenić, czy błąd jest spowodowany jakimś tymczasowym błędem i wydajemy oprogramowanie, czy nie. Ale nie blokuje nam to pracy.

Dowiedz się, jak podnieść stabilność tego typu testów: Nie działa środowisko? 🤬 Stabilne testowanie integracyjne z infrastrukturą 🧘‍♂️ Projekt Testcontainers

(S) Self-validating

Tetsy powinny dawać jasną i klarowną informację, czy przeszły, czy nie: PASS lub FAIL. Jeżeli się nie udał, to powinien dać informację:

  • co się stało
  • i dlaczego się stało

Dlatego tak bardzo ważne jest poprawne i jednoznaczne nazywanie testów oraz towarzyszącym ich asercji. Później raport z testów będzie wyglądał w taki sposób, że możemy bardzo szybko zorientować się co poszło nie tak i dlaczego.

W zasadzie self-validating chodzi też o to, zeby przygotować raport z testów w sposónb automatyczny. Żeby nie musieć ręcznie interpretować raportu z przebiegu testów. Wynik powinien być kwantyfikowany (mierzalny). Wtedy przykładając do wyniku jakąś miarę, możemy w sposób automatyczny określić rezultat przebiegu testów, bez manualnego sprawdzania.

Psst… Interesujący artykuł?

Jeżeli podoba Ci się ten artykuł i chcesz takich więcej – dołącz do newslettera. Nie ominą Cię materiały tego typu.

.

(T) Timely

Zasada Timely mówi o tym, żeby testy pisane były zawsze na czas – razem z kodem, a nie dopisywane po tym, jak wdrożymy funkcjonalność.

Wyobraź sobie, że piszesz kod, a potem po jakimś czasie zabrakło testów i „trzeba dopisać testy”. Testy są współczynnikiem jakości. Jeżeli piszesz kod razem z testem, gwarantujesz pewną jakość wytworzonej pracy, że implementacja działa poprawnie.

Są techniki, które wspomagają tego typu zachowanie TDD (Test-Driven Developent), gdzie najpierw powstają testy, a później implementacja. To się nazywa testowanie przed kodem.

Moim zdaniem nie ma niczego złego w napisaniu testów po kodzie, po powstaniu implementacji, ale w ramach tego samego zestawu zmian wciąganego do repozytorium. Możemy się wtedy wspomóc raportem Code Coverage i Mutation Coverage.

Ważne jest to, żeby testy produktowa w procesie wytwarzania oprogramowania, razem z kodem, żeby wprowadzić dobrą jakość.

Przykład testów z zachowaniem zasad F.I.R.S.T.

Oto raport z przykładowego projektu (który znajdziesz w Programie Java Developera). Wprowadziłem tam zmianę, która popsuła funkcjonalność. Raport mówi mi, że testy nie przechodzą:

Sprawdźmy czy ten zestaw testów odpowiada regułom F.I.R.S.T.:

  • F – OK
    Zestaw testów wykonuje się relatywnie szybko. Czas wykonania bez inicjalizacji to ok 15ms.
  • I – OK
    Wprowadzana zmiana zapaliła tylko część testów. Testy nie zależą od żadnego stanu.
  • R – OK
    Uwierz na słowo, że testy można uruchamiać ponownie i każde uruchomienie daje tan sam rezultat.
  • S – OK
    Nie muszę za każdym razem ręcznie sprawdzać wyniku działania, np. czytać logów aplikacji, żeby dowiedzieć się co się stało. Z nazw przypadków testowych szybko orientuję się co popsułem. Szczegóły widzę w opisie asercji.
  • T – OK
    Testy zostały napisane „na czas”, razem z kodem.

Podsumowanie

Jakie są zasady pisania dobrych testów?

Zestaw zasad, które sprawią, że Twój zestaw testów będzie bardziej użyteczny w projekcie zawiera akronim F.I.R.S.T.

Co oznacza F.I.R.S.T.?

Akronim F.I.R.S.T. zbiera ogólnie przyjęte dobre zasady pisania dobrych testów. Są to:
(F)ast – Szybkie, często uruchamiane testy.
(I)ndependent/Isolated – Testują pojedynczy możliwie przypadek. Są wyizolowane i niezależne od siebie, nie zależą od stanu ustawionego w innych testach, ani nie mają efektów ubocznych. Uruchamiane w losowej kolejności dają ten sam wynik.
(R)epeatable – Takie same wyniki za każdym razem. Testy są stabilne i deterministyczne.
(S)elf-validating – Brak manualnej interpretacji. Dają jasny i klarowny raport z testów, co nie działa i dlaczego.
(T)imely – Napisane na czas, wraz z implementacją, jako jedna zmiana w projekcie. Wspomóż się TDD lub napisz testy po kodzie. Praktyka czyni mistrza.

Wpis który czytasz to zaledwie fragment wiedzy zawartej w Programie szkoleniowym Java Developera od SoftwareSkill. Mamy do przekazania sporo usystematyzowanej wiedzy z zakresu kluczowych kompetencji i umiejętności Java Developera. Program składa się z kilku modułów w cotygodniowych dawkach wiedzy w formie video.

Podoba Ci się ten artykuł? Weź więcej.

Jeżeli uważasz ten materiał za wartościowy i chcesz więcej treści tego typu – nie przegap ich i otrzymuj je prosto na swoją skrzynkę. Nawiążmy kontakt.

.

Gdybyś potrzebował jeszcze więcej:

Jesteś Java Developerem?

Przejdź na wyższy poziom wiedzy 
„Droga do Seniora” 🔥💪

Jesteś Team Leaderem? Masz zespół?

Podnieś efektywność i wiedzę swojego zespołu 👌

Piguła wiedzy o najlepszych praktykach testowania w Java

Pobierz za darmo książkę 100 stron o technikach testowania w Java

Dyskusja