In this article, I will describe why testing application code is so important, describe how to create different types of tests, and show an example of a JUnit test for Java. I always say, if you want to sleep soundly, write application code tests. By tests I mean various types of tests, e.g. unit tests, integration tests, automatic and manual tests. There are several techniques for writing tests.

Preface

People are divided into those who write the tests and those who will write them. We all know what the production application tests performed by mBank looked like - niebezpiecznik.pl (5/8/2020) – we don't do that, we want to avoid it.

Techniques for writing tests

At the beginning I mentioned different types of tests, I will discuss them in more detail later. Various test writing techniques can be added to the test types.

TDD – Test Driven Development

Najpopularniejszą techniką pisania testów jest Test Driven Developmnet – TDD. Technika TDD zakłada w pierwszej kolejności pisanie testów, a następnie pisanie właściwej klasy, którą testujemy. Dzięki temu wymuszamy pisanie testów do tworzonego kodu. TDD wyrabia dobry nawyk dzięki, któremu nasz kod źródłowy “od razu” posiada testy. Polecam książkę TDD. Sztuka tworzenia dobrego kodu.

“Technika” dopisywania testów

Kolejną “techniką” pisania testów jest dodawanie testów do już istniejącego kodu źródłowego w trakcie refaktoryzacji lub z powodu braku testów. Nie jest, to idealne rozwiązanie, ale tworzenie testów nawet dla istniejącego kodu jest lepszym rozwiązaniem niż brak testów. Proces refaktoryzacji wspartej TDD jest opisany w “Rozdział 31. Refaktoryzacja” w książce TDD. Sztuka tworzenia dobrego kodu.

Why write tests?

Let me start by saying that the lack of tests may indicate that we do not understand the source code being created, because good tests require knowledge of the code we are testing. If we understand the code we are testing, it means we will be able to modify it. I often tell my students that if you want to sleep soundly, write unit tests. My colleague will think ten times before modifying our code, because the tests may stop working and will have to be corrected.

The only constant thing in code is code variability. Tests allow you to modify existing code without worrying that your change will break something in the code. If this is the case, the tests will show such an error and we will be able to detect and correct such an error already at the stage of code creation, not in production. We will avoid the situation with our favorite mBank – niebezpiecznik.pl (5/8/2020).

Who writes the tests?

W procesie tworzenia testów należy wyróżnić programistów oraz testerów. Dlaczego nie wystarczą testy pisane przez programistów? Powodów jest kilka, pierwszym z nich jest to, że programista testując swój kod wie jakie warunki i miejsca omijać w testowanym kodzie, no i oczywiście, kto będzie znajdował błędy w swoim idealnym kodzie? Testerzy często tworzą testy dla osób nietechnicznych, które chcą zweryfikować jak działa zamawiane oprogramowanie. Dodatkowo tester “dostaje wynagrodzenie” za znalezienie jak największej liczby błędów.

Programmer

Testy napisane przez programistów weryfikują poprawność działania tworzonego kodu z perspektywy języka programowania, jak również logiki biznesowej aplikacji, np. wykonanie przelewu bankowego jest logiką biznesową aplikacji.

Programiści w większości przypadków tworzą testy automatyczne. Dla każdego testu należy zdefiniować dane wejściowe oraz oczekiwany wynik, aby zdefiniować takie dane oraz wynik musimy wiedzieć jaka jest logika naszej aplikacji. Innymi słowy, tworząc poprawne testy pokazujemy, że wiemy jak działa testowana przez nas aplikacja.

Tester

Tworzą testy, aby głównie zweryfikować logikę biznesową aplikacji oraz testują tzw. warunki brzegowe, np. czy można wykonać przelew na ujemną kwotę i/lub datę z przeszłości? Testerzy tworzą zarówno testy automatyczne jak również manualne.

Kategorie testów

Manual tests

Polegają np. na klikaniu w stronę internetową banku w celu wykonania przelewu. Jeżeli tester znajdzie błąd podczas wykonywania przelewu zgłosi go – za pomocą systemu do zgłaszania błędów, np. JIRA – programiście, który będzie musiał taki błąd poprawić w późniejszym czasie, nie koniecznie od razu. Idealną sytuacją jest, w której tester posiada scenariusze testowe, które, to krok po kroku opisują, co należy zrobić, aby wykonać przelew bankowy. Na tej podstawie można odtworzyć sytuację, która wywołała błąd, w przeciwnym wypadku błąd może nie zostać nigdy odtworzony i naprawiony.

Automatic tests

Wymagają użycia narzędzia, za pomocą którego można zaprogramować, zautomatyzować sekwencję czynności, które doprowadzą np. do wykonania przelewu bankowego. Większość testów automatycznych tworzonych przez testerów jest testami integracyjnymi.

Do testów automatycznych testerzy mogą używać takie narzędzia jak Selenium (testy www), Postman (testy REST API) lub JMeter (testy wydajnościowe). Kroki niezbędne do wykonania testu powinny być spisane w scenariuszu testowym. Za pomocą ww. narzędzi tester nagrywa, koduje test automatyczny, który następnie jest odtwarzany dowolną ilość razy w dowolnym momencie na wcześniej zdefiniowanym zbiorze danych.

Z założenia testy tworzone przez programistów są testami automatycznymi. Do tworzenia takich testów programiści używają framework’ów do testowania kodu źródłowego, czyli klas języka Java. Najpopularniejszym obecnie framework’iem jest JUnit. Jak łatwo korzysta się z JUnit w IntelliJ napisałem wcześniej w tym artykule. Testy automatyczne tworzone przez programistę możemy podzielić na testy integracyjne i jednostkowe.


Zanim omówię testy kodu źródłowego z perspektywy programisty zachęcam do zapoznania się z informacjami o klasach w języku Java, Pierwsza klasa – kod Java, IntelliJ, krok po kroku oraz informacjami o metodach w języku Java, Działania w pierwszej klasie – kod Java, IntelliJ, krok po kroku. Testy kodu źródłowego pisane przez programistę testują metody w klasach.


JUnit test – praktyczny przykład

Środowiska programistyczne takie, jak IntelliJ domyślnie wspierają tworzenie testów dla naszego kodu. Wystarczy ustawić kursor na wybranej nazwie klasy i wcisnąć ALT+Enter, wyświetli nam się opcja “Create Test” i/lub wcisnąć ALT+Insert w dowolnym miejscu w kodzie klasy i wybrać opcję “Test…”. Następnie będziemy mogli wybrać bibliotekę do testów (na chwilę obecną domyślny jest JUnit5) oraz metodę, którą chcemy przetestować, będzie ona dostępna do wyboru na liście.

Wybór biblioteki do testów i metod do testowania - IntelliJ
Wybór biblioteki do testów i metod do testowania - IntelliJ
« z 3 »

Test jednostkowy – co testować?

Programista pisząc testy jednostkowe, testuje poprawność działania metod w klasie, głównie metod publicznych. W jaki sposób testuje się poprawność działania metody? W języku Java metoda może przyjmować parametry, zwracać wynik i/lub rzucać wyjątek i właśnie te elementy są testowane przez programistę. Testowanie działania metody linijka po linijce nie miało by większego sensu.

Test jednostkowy – jak testować?

Mając powyższą wiedzę programista definiuje dane wejściowe do metody oraz sprawdza wartość zwracaną z metody. Dobrą praktyką jest podzielenie testu na sekcje given, when, then – nazwy są samo opisujące się. W tych sekcjach programista umieszcza logikę testu jednostkowego.

  • W sekcji given powinna być klasa, którą testujemy oraz wartości dla parametrów testowanej metody – dane wejściowe – w tym przypadku BankService oraz BankAccount.
  • W sekcji when powinna zostać wywołana metoda, którą testujemy transfer(), przekazujemy do niej wartości dla parametrów (fromBankAccount oraz toBankAccount), a następnie przypisujemy wartość zwracaną transferred.
  • W sekcji then sprawdzamy wartość zwracaną z metody transfer() za pomocą mechanizmu assercji z framework’a JUnit – w tym przypadku assertFalse().
Po co w ogóle testować kod aplikacji? - Test jednostkowy JUnit
Po co w ogole testowac kod aplikacji? – Test jednostkowy JUnit

Integration test

Testy integracyjne programista pisze dla wielu współpracujących ze sobą klas i ich metod, grupuje różne funkcje kodu w celu przetestowania tzw. pełnej ścieżki dla danego przypadku użycia, który może być opisany w scenariuszu testowym. W dużym uproszczeniu można powiedzieć, że w sekcji given będzie więcej obiektów (CurrencyConverterService i BankService), a w sekcji when będzie więcej wywołań metod (balance() i convert()), których wyniki będą wzajemnie przekazywane (BigDecimal balance i BigDecimal balanceInEuro).

Po co w ogóle testować kod aplikacji? - Test integracyjny JUnit
Po co w ogole testowac kod aplikacji? – Test integracyjny JUnit

Piramida testów

Matrin Fowler - The Practical Test Pyramid
Matrin Fowler – The Practical Test Pyramid

Na koniec umieszczę link do artykułu o piramidzie testów z praktycznym przykładem oraz obszernym wytłumaczeniem roli i znaczenia testów – https://martinfowler.com/articles/practical-test-pyramid.html.