Poradnik

Testy integracyjne z wykorzystaniem WireMock w Spring Boot

java laptop

Jeżeli kiedykolwiek Twój system integrował się z zewnętrznymi usługami firm trzecich, to doskonale wiesz, że droga ta może być kręta i wyboista. Realizujesz serwisy w Javie z wykorzystaniem Spring Boot? Sprawdź jak może Ci pomóc WireMock.

Obrazek mobilny
Obrazek desktopowy

Analityk Biznesowo-Systemowy

Aplikuj

Źle zrealizowane kontrakty, niewłaściwie działająca autoryzacja czy uwierzytelnianie, błędnie chwycone wyjątki, timeout, internal server error – to tylko niektóre pułapki, które często są wyłapywane dopiero w środowisku produkcyjnym.

Aby temu zapobiec i ułatwić sobie pracę, warto przyjrzeć się WireMock: narzędziu, dzięki któremu w prosty sposób można zamockować zewnętrzne API w testach integracyjnych i nie tylko.

Use Case

Wyobraź sobie sytuację, że realizujesz nowy mikroserwis obsługi płatności w sklepie internetowym, w którym będziesz komunikować się z bankiem w celu blokady środków na koncie płatnika. Wszystkie dokumentacje z bankiem zostały zatwierdzone, kontrakty ustalone, umowy podpisane, jest zielone światło na realizację systemu. 

Musi być on jednak bardzo dobrze przetestowany. Z testami jednostkowymi nie powinno być problemów, ale jak dobrze przetestować komunikację z systemem bankowym? Pokażę Ci, jak łatwo napisać takie testy z WireMock.

Inicjalizacja projektu

W celu zainicjalizowania projektu wchodzisz na stronę https://start.spring.io/, na której w prosty sposób możesz wygenerować projekt ze wszystkim zależnościami.

Na start potrzebujesz: 

  • Spring Web do realizacji REST API, 
  • Spring Reactive Web do komunikacji z zewnętrznym serwisem, 
  • Contract Stub Runner, aby w łatwy sposób korzystać z WireMock.
spring initializr

Stwórz pierwszy kod: 

Klasa reprezentująca żądanie blokady środków na podstawie karty płatniczej w banku:

Serwis do komunikacji z bankiem:

Klasa reprezentująca odpowiedź z banku na żądanie blokady środków:

Słownik “statusy płatności”:

Serwis do realizacji płatności oraz walidowania odpowiedzi z banku:

API przyjmujące dane ze sklepu internetowego:

Po wykonaniu tych kroków efektem jest prosty kod, który odbiera informacje o potrzebie zrealizowania płatności ze sklepu internetowego, komunikuje się z bankiem i waliduje jego odpowiedź.

Pierwsze testy integracyjne

Zacznij od napisania poprawnej odpowiedzi w formacie JSON, którą powinien zwrócić nam bank, gdy blokada środków się powiodła:

Napisz test integracyjny, w którym przygotujesz mock z pozytywną odpowiedzią banku.

W celu zrealizowania testu integracyjnego i uruchomienia go za pomocą kontenera Spring Boot należy wykorzystać adnotację @SpringBootTest.

Kolejno podaj adnotację, dzięki której zostanie uruchomiony WireMock: @WireMockTest(httpPort = 8081). httpPort określa, na jakim porcie ma zostać uruchomiony WireMock. Jeżeli nie podasz tej wartości, port będzie wylosowany.

Następnie wstrzykujesz swój serwis do komunikacji z bankiem, a także w łatwy sposób poprzez adnotację @DynamicPropertySource, podmieniasz adres URL do banku. W tym wypadku wskazujesz mu adres http://localhost:8081, na którym uruchomiony jest WireMock.

Zaczytaj poprawną odpowiedź. W tym celu możesz wykorzystać klasę i metodę dostarczoną przez WireMock, ułatwiającą odczytywanie odpowiedzi z pliku (IOUtils.resourceToString). 

Trzymanie wartości odpowiedzi w plikach poprawia czytelność testu. Jeżeli umieścisz tę wartość jako zwykły tekst, test może mocno się rozrosnąć i stać się uciążliwy w utrzymaniu oraz mało czytelny.

 Kolejno konfiguruj HTTP mock z wykorzystaniem WireMock:

W powyższy sposób zaadresowałaś/eś do WireMock utworzenie metody POST na adresie http://localhost:8081/charge. W odpowiedzi na request zostanie zwrócony status 200 z nagłówkiem „Content-Type”, „application/json” oraz body, które napisaliśmy parę linijek wyżej. 

Sprawdź, czy to faktycznie działa: wywołaj serwis oraz dopisz sprawdzenie wartości:

Klasa z pierwszym testem powinna wyglądać tak:

Test przeszedł pozytywnie.

Zrealizuj teraz kolejny test, aby sprawdzić, czy odpowiedź HTTP 500 – internal server error, zwróci w kodzie wyjątek:

W WireMock w bardzo intuicyjny sposób możemy manipulować kodami http w odpowiedziach i testować różne ścieżki negatywne. 

Co jeszcze potrafi WireMock? Kilka przydatnych funkcjonalności

Tworzenie endpointów różnych typów

Endpointy (GET, POST, PUT, DELETE) możesz z pomocą WireMock tworzyć z wykorzystaniem zarówno equal, jak i regexp:

Oczekiwanie odpowiedniej wartości w żądaniu HTTP. 

Jeżeli nie zostaną spełnione warunki, mock zwróci status 404 – Not Found, ponieważ nie był w stanie dopasować zapytania z odpowiedzią.

Parametryzowanie zwracanej wartości

WireMock posiada obszerny mechanizm parametryzowania zwracanej odpowiedzi. W tym celu musisz użyć specjalnej składni. Zapis ten musi się zaczynać i kończyć dwiema klamrami; podczas zwracania body algorytm podmieni zapis pod zadane wartości, np.:

Gdy chcemy, aby nasza odpowiedź zawierała wartości z body requestu, możemy wykorzystać JSONPath, np:

Podane przykłady to tylko garstka możliwości parametryzowania odpowiedzi; po więcej zapraszam na stronę oficjalnej dokumentacji: WireMock Response Templating

Opóźnienie odpowiedzi/symulowanie usterek serwisu

Dużym plusem wykorzystania WireMock w testach integracyjnych jest możliwość wygenerowania wadliwego zachowania usługi: możesz wtedy bardzo łatwo wychwycić i załatać błąd przed wdrożeniem serwisu na produkcję. Kilka przykładów poniżej.

Symulowanie opóźnienia o 60 sekund:

Symulowanie zerwania połączenia:

Symulowanie pustej odpowiedzi:

Weryfikowanie żądań

Wszystkie żądania i zapytania ze strony aplikacji Wiremock zapisuje w pamięci, dzięki czemu w łatwy sposób w teście możesz sprawdzić, czy endpoint został wywołany określoną ilość razy.

To tylko część możliwości, jaką daje WireMock z Spring Boot. Aby zgłębić temat i dowiedzieć się więcej — zapraszam do oficjalnej dokumentacji WireMock Docs

Kod, który został zrealizowany w artykule, dostępny jest na Github:
Github Payments

Zdjęcie główne pochodzi z unsplash.com.

Senior Developer w Tpay

Od nastoletnich lat informatyka była dla niego pasją. Uwielbia majsterkować nie tylko z językami programowania i technologiami. Z pracy stara się czerpać jak najwięcej radości i satysfakcji.

Podobne artykuły