Testy integracyjne z wykorzystaniem WireMock w Spring Boot

geek.justjoin.it 1 rok temu

Jeżeli kiedykolwiek Twój system integrował się z zewnętrznymi usługami firm trzecich, to doskonale wiesz, iż 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.

Ź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, za pomocą którego w prosty sposób można zamockować zewnętrzne API w testach integracyjnych i nie tylko.

Use Case

Wyobraź sobie sytuację, iż 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.

Stwórz pierwszy kod:

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

public record BankChargeRequest(Integer amount, String cardNumber, String cvc){ }

Serwis do komunikacji z bankiem:

@Service public class BankPaymentService { @Value("${bank.url}") private String bankUrl; public BankChargeResponse sendPaymentToBank(BankChargeRequest dto){ WebClient webClient = WebClient.builder().baseUrl(bankUrl).build(); return webClient .post() .uri("/charge") .body(BodyInserters.fromValue(dto)) .retrieve() .bodyToMono(BankChargeResponse.class) .block(); } }

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

public record BankChargeResponse(String id, PaymentStatus status, String message) { }

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

public enum PaymentStatus { ACCEPTED, FAILED }

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

@Service public class PaymentService { private final BankPaymentService bankPaymentService; public PaymentService(BankPaymentService bankPaymentService) { this.bankPaymentService = bankPaymentService; } public BankChargeResponse createPayment(BankChargeRequest request) throws PaymentStatusInvalidException, PaymentFailedException { BankChargeResponse response = bankPaymentService.sendPaymentToBank(request); validResponse(response); return response; } private void validResponse(BankChargeResponse response) throws PaymentFailedException, PaymentStatusInvalidException { if (response == null) { throw new PaymentFailedException(); } if (response.status() == null) { throw new PaymentStatusInvalidException(); } if (PaymentStatus.FAILED.equals(response.status())) { throw new PaymentFailedException(); } } }

API przyjmujące dane ze sklepu internetowego:

@RestController @RequestMapping("/payments") public class PaymentsController { private PaymentService paymentService; public PaymentsController(PaymentService createPayment) { this.paymentService = createPayment; } @PostMapping public PaymentCreateResponse create(BankChargeRequest dto) throws PaymentStatusInvalidException, PaymentFailedException { BankChargeResponse bankChargeResponse = paymentService.createPayment(dto); return new PaymentCreateResponse(bankChargeResponse.id()); } }

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ź.

Idź do oryginalnego materiału