Zespół sprzedaży kredytów zmienił format jednego pola w odpowiedzi API. U siebie przetestował wszystko — zielono. Zespół uruchomienia testował na mocku, który wciąż zwracał stary format — też zielono. Kontrakt między systemami złamał się bezgłośnie, a pierwszy czerwony sygnał zobaczył klient na produkcji. Testy kontraktowe API istnieją dokładnie po to, żeby ten sygnał pojawił się w CI: kilka tygodni wcześniej i o rząd wielkości taniej.
Mock kłamie grzecznie
Zacznijmy od uczciwej diagnozy. W organizacji po transformacji shift-left każdy zespół testuje swój komponent szybko i blisko kodu — a sąsiednie systemy zaślepia. Mock robi dokładnie to, co mu kazano: zwraca odpowiedź, którą autor testu założył, że sąsiad zwróci. I tu jest problem — mock nie jest złośliwy, jest grzeczny. Nigdy nie zaprotestuje, że sąsiedni system od trzech sprintów zwraca inną strukturę. Będzie uprzejmie potwierdzał założenia sprzed pół roku, a test na nim będzie równie uprzejmie zielony.
Efekt znamy wszyscy: każdy moduł osobno działa, a proces biznesowy pęka na styku. Defekt na granicy dwóch systemów nie ma właściciela, nie ma testu i nie ma czerwonego sygnału — obie strony testowały własne wyobrażenie tej samej granicy. Anatomię tego zjawiska rozkładamy szczegółowo w schemacie problemu; tutaj skupimy się na najskuteczniejszym pojedynczym narzędziu, które tę lukę zamyka.
Mock koduje założenia jednego zespołu o zachowaniu drugiego. Kontrakt koduje uzgodnienie obu zespołów — i jest weryfikowany po obu stronach. To cała różnica, ale zmienia wszystko.
Czym jest kontrakt danych — i dlaczego schema to za mało
Kontrakt danych to jawna, wersjonowana umowa między konsumentem a dostawcą API. „Jawna" znaczy: zapisana w repozytorium jako artefakt, nie ukryta w kodzie dwóch zespołów i pamięci trzech osób. „Wersjonowana" znaczy: każda zmiana jest widoczna, porównywalna i świadomie zaakceptowana. Umowa obejmuje co najmniej cztery warstwy:
- Pola i typy — co dokładnie płynie przez styk, w jakim formacie, które pola są wymagane, a które opcjonalne.
- Identyfikatory — które klucze spinają encje po obu stronach i jaki jest ich cykl życia.
- Niezmienniki — warunki, które muszą być spełnione zawsze: kwota większa od zera, waluta z ustalonego słownika, status z zamkniętej listy.
- Semantyka — co dane znaczą. To warstwa, którą walidacja schemy w ogóle nie widzi, a na której pęka najwięcej integracji.
Konkret z procesu kredytowego — głównej ścieżki sprzedaż → uruchomienie → spłata → zamknięcie. System sprzedaży tworzy wniosek_id i kwotę; system uruchomienia wymaga obu tych pól i zwraca umowa_id. Schema powie nam, że kwota to liczba dziesiętna. Nie powie, czy to kwota brutto z prowizją, czy netto do wypłaty; czy w przypadku kredytu konsolidacyjnego — który jest gałęzią grafu procesu, z własnym kontraktem na tej gałęzi — obejmuje ona spłacane zobowiązania, czy tylko nową gotówkę. Oba systemy mogą przejść walidację schemy i policzyć klientowi dwa różne harmonogramy. Dlatego dobry kontrakt opisuje również znaczenie pól i przykładowe pary żądanie–odpowiedź dla wariantów procesu, nie tylko kształt JSON-a.
Consumer-driven contracts w praktyce
Najlepiej sprawdzającym się modelem pracy jest kontrakt sterowany konsumentem (consumer-driven contract). Logika jest prosta: to konsument wie, czego naprawdę używa. Dostawca API może udostępniać czterdzieści pól, ale jeśli system uruchomienia konsumuje trzy, kontrakt powinien chronić te trzy — precyzyjnie i bez szumu.
Jak to wygląda operacyjnie:
- Kto pisze kontrakt: zespół konsumenta spisuje swoje realne oczekiwania — pola, których używa, wartości, na których polega, warianty odpowiedzi, które obsługuje. Dostawca recenzuje i akceptuje. Kontrakt staje się wspólną własnością obu stron, z jednym uzgodnionym miejscem prawdy.
- Gdzie żyje: w repozytorium — jako plik podlegający code review i wersjonowaniu, a przy większej skali w brokerze kontraktów. Zmiana kontraktu przechodzi ten sam proces co zmiana kodu: pull request, przegląd, zgoda obu stron.
- Jak testują obie strony: konsument w swoim CI weryfikuje, że jego kod poprawnie obsługuje odpowiedzi zgodne z kontraktem. Dostawca w swoim CI weryfikuje, że jego rzeczywista implementacja nadal spełnia wszystkie kontrakty wszystkich konsumentów. Złamanie kontraktu blokuje pipeline dostawcy — zanim zmiana dotrze do wspólnego środowiska.
To ostatnie zdanie jest sednem metody: dostawca dowiaduje się, że psuje konsumenta, we własnym CI, kilka minut po zmianie — a nie na testach integracyjnych za dwa tygodnie ani na produkcji za miesiąc.
Wirtualizacja usług — z tego samego kontraktu co produkcja
Skoro kontrakt jest jawny i maszynowo czytelny, można z niego zrobić coś więcej niż test: można z niego wygenerować zaślepkę. To fundamentalna różnica względem ręcznie pisanych mocków. Stub zbudowany z kontraktu zachowuje się tak, jak dostawca zobowiązał się zachowywać — a ponieważ dostawca jest w swoim CI weryfikowany względem tego samego kontraktu, dryf między zaślepką a rzeczywistością przestaje być cichy. Gdy dostawca zmieni API, najpierw pęknie jego weryfikacja kontraktu, a nie test konsumenta na nieaktualnym mocku. Zaślepka i produkcja mają wspólne źródło prawdy.
I tu uczciwe zastrzeżenie, bo bez niego ten artykuł byłby marketingiem: wirtualizacja nie zastępuje testu na realnym połączeniu wszędzie. Kontrakt weryfikuje strukturę, niezmienniki i uzgodnioną semantykę — ale nie wykryje problemów wydajnościowych styku, niespójności danych między środowiskami ani zachowań, których nikt nie przewidział w umowie. Tam, gdzie styk jest głównym źródłem ryzyka procesu — np. przekazanie uruchomionej umowy do modułu spłat — testujemy na realnej integracji, a wirtualizujemy styki tańsze w ryzyku. To świadoma decyzja podejmowana per styk, nie domyślny skrót; jak dobieramy tę granicę, opisujemy w materiale „Jak to działa naprawdę".
Narzędzia i osadzenie w procesie
Warstwa wykonania jest dziś dojrzała i nie trzeba tu niczego wymyślać. W projektach naszych klientów testy kontraktowe i API uruchamiamy najczęściej na trzech narzędziach: REST Assured (asercje na żądaniach i odpowiedziach HTTP w ekosystemie JVM, naturalny wybór, gdy testy żyją blisko kodu backendu), Karate (czytelne scenariusze API z wbudowanymi dopasowaniami struktur — niski próg wejścia dla całego zespołu) oraz SoapUI tam, gdzie integracje obejmują usługi SOAP i starsze systemy, co w bankowości jest normą, nie wyjątkiem.
Samo narzędzie to jednak mniejsza połowa sukcesu. Test kontraktowy, który żyje w prywatnym repozytorium jednego zespołu, chroni jeden styk z perspektywy jednej strony. Żeby kontrakty chroniły proces, muszą być reużywalne i widoczne: w naszym podejściu kroki kontraktowe trafiają do wspólnej biblioteki kroków BDD (utrzymywanej w Smart.Edytor), gdzie „zweryfikuj kontrakt sprzedaż→uruchomienie" jest tym samym klockiem dla każdego scenariusza, który przez ten styk przechodzi. Wykonaniem steruje Smart.Runner — te same kroki są uruchamiane w pipeline przy każdej zmianie i na żądanie przed wydaniem, a ich wyniki zasilają metryki styków wspierające Business Journey Coverage. Więcej o modułach platformy: produkty STS.
Typowe błędy wdrożeń contract testingu
Cztery sposoby, żeby mieć testy kontraktowe i nie mieć z nich nic
- Kontrakt = kopia schemy. Wygenerowanie kontraktu z OpenAPI dostawcy odtwarza jego punkt widzenia — z całym szumem i bez semantyki. Taki „kontrakt" przechodzi zawsze, bo testuje dostawcę względem samego siebie.
- Brak wersjonowania. Kontrakt bez historii wersji i procesu zmiany to dokumentacja, nie umowa. Nie wiadomo, kto na czym polega i którą wersję kto spełnia — a przy pierwszej spornej zmianie wraca ustalanie prawdy mailami.
- Testowanie wyłącznie happy path. Integracje pękają na przypadkach brzegowych: odrzucony wniosek, timeout, pusta lista zobowiązań w wariancie konsolidacyjnym, kod błędu, którego konsument nie zna. Kontrakt, który opisuje tylko ścieżkę sukcesu, chroni tylko ścieżkę sukcesu.
- Kontrakt bez właściciela. Gdy nikt nie odpowiada za przegląd i ewolucję kontraktów, po roku testują one stan sprzed roku. Kontrakty potrzebują gospodarza — u nas tę rolę pełni zespół platformowy jakości, właściciel biblioteki kroków i modelu procesu.
Wspólny mianownik tych błędów: traktowanie contract testingu jako narzędzia do wdrożenia zamiast praktyki do utrzymania. Narzędzie instaluje się w tydzień. Praktyka — uzgadnianie, recenzowanie i egzekwowanie umów między zespołami — wymaga procesu i właściciela.
Od czego zacząć
Nie od narzędzia i nie od wszystkich API naraz. Sekwencja, która działa u naszych klientów:
- Wybierz jeden krytyczny proces biznesowy — taki, którego awaria na produkcji boli najbardziej. W banku naturalnym kandydatem jest ścieżka kredytowa: sprzedaż → uruchomienie → spłata → zamknięcie.
- Zmapuj styki na tej ścieżce. Które systemy przekazują sobie dane, jakie identyfikatory je spinają, gdzie historycznie koncentrowały się incydenty. Ta mapa zwykle mieści się na jednej kartce — i zwykle nikt jej wcześniej nie narysował.
- Spisz i uruchom trzy pierwsze kontrakty — dla styków o największym ryzyku. Z semantyką i przypadkami brzegowymi, wpięte w CI obu stron. Trzy działające kontrakty na krytycznym procesie dadzą więcej niż trzydzieści wygenerowanych ze schemy.
Po miesiącu takiej pracy organizacja ma coś, czego wcześniej nie miała: pierwsze styki, o których wiadomo — a nie tylko się zakłada — że dotrzymują umowy. A pierwsza zmiana API zatrzymana w CI zamiast na produkcji jest zwykle najlepszym argumentem za rozszerzeniem praktyki na kolejne procesy.
Chcesz zobaczyć mapę styków własnego procesu zamiast czytać o cudzych? Zacznij od diagnozy problemu: dlaczego zielone testy nie chronią procesów →