W momencie, gdy uruchomiłem testy kontraktowe Specmatic przeciwko mojemu „ukończonemu” klonowi Zerodha opartego na stosie MERN, narzędzie wykryło pięć realnych błędów, których moje ręczne sprawdzanie nigdy nie wychwyciło. Z 178 wygenerowanych przypadków testowych, zestaw testowy wykazał błędy w API, które uderzyłyby w realnych użytkowników — nieprawidłowe typy danych, awarie przy błędnie sformatowanych poświadczeniach, cichą korupcję danych, nieidempotentne procesy rejestracji oraz zmianę kontraktu powodującą błędy, która zatrzymała proces CI.
Jak błędy umknęły testom manualnym
Projekt łączył backend w Node.js, frontend w React, bazę danych MongoDB oraz Razorpay do płatności. Przeszedłem przez procesy rejestracji i płatności ręcznie i wszystko wydawało się działać. Testowanie manualne sprawdza jednak tylko tzw. „happy path”: potwierdza, że kod zachowuje się poprawnie, gdy użytkownicy postępują zgodnie z zamierzonymi krokami. Nie dowodzi ono jednak, że usługa przetrwa błędnie sformatowane żądania lub nieoczekiwane zachowanie klienta.
Kiedy skierowałem Specmatic na istniejący kod, kontrakt — czyli jawny opis kształtu żądania i odpowiedzi każdego punktu końcowego (endpointu) — posłużył jako źródło prawdy. Narzędzie wygenerowało następnie ogromną macierz pozytywnych i negatywnych scenariuszy, z których wielu żaden tester manualny nigdy by nie wymyślił.
Pięć wykrytych wad
- Luki w walidacji danych wejściowych – Endpoint
/newOrderprzyjmował liczby dziesiętne i ciągi znaków dla polaquantity, mimo że kontrakt wymagał liczby całkowitej. Wygenerowane testy wysyłające błędne typy spowodowały nieprawidłowe działanie API. - Nieobsłużone błędy logowania – Przekazanie błędnie sformatowanych poświadczeń do trasy uwierzytelniania wywołało wyjątek w czasie wykonywania (runtime exception), ponieważ w kodzie brakowało sprawdzania typów.
- Cicha korupcja danych płatności – Endpoint
/verify-paymentpozwalał na podawanie wartości logicznych (boolean) dla polaamount. Gdy do systemu przesunęła się wartośćtrue, baza danych zarejestrowała udaną płatność o wartości zero, po cichu zawyżając wyniki przychodów. - Brak idempotencji – Ponowne uruchomienie procesu rejestracji w zestawie testowym zakończyło się niepowodzeniem, ponieważ endpoint próbował ponownie utworzyć istniejącego użytkownika, zamiast w sposób kontrolowany obsłużyć duplikat.
- Wykrycie zmiany kontraktu powodującej błędy – Celowo zmieniłem typ danych w kontrakcie. Potok CI natychmiast odrzucił tę zmianę, zapobiegając wydaniu wersji powodującej błędy.
Dlaczego testowanie kontraktowe jest ważne dla potoków CI
- Testowanie negatywne na dużą skalę – Większość ze 178 przypadków stanowiły dane brzegowe. Pisanie ich ręcznie byłoby niezwykle czasochłonne.
- Bezpieczeństwo dla klientów zewnętrznych – Kontrakty definiują to, co usługa obiecuje zewnętrznym konsumentom. Jeśli implementacja odbiega od wzorca, test kontraktowy kończy się niepowodzeniem, chroniąc aplikacje zależne.
- Szybka pętla zwrotna – Brama CI zatrzymała zmianę powodującą błędy, zanim mogła zostać scalona, oszczędzając zespołowi kosztownego wycofania zmian (rollback).
- Poprawa jakości kodu – Dodanie endpointu actuator i zapewnienie idempotencji procesu rejestracji były niezbędnymi krokami, aby umożliwić testowanie kontraktowe, co z kolei wzmocniło usługę.
Kompromis, który programiści powinni rozważyć
Testowanie kontraktowe zwiększa nakład pracy związany z utrzymaniem. Specyfikacja musi być zsynchronizowana z kodem, a proces generowania testów może wydłużyć czas budowania projektu. Zespoły muszą zdecydować, czy dodatkowe bezpieczeństwo uzasadnia dodatkowy wysiłek, szczególnie w przypadku mniejszych projektów, gdzie testowanie manualne wydaje się wystarczające.
Na co zwrócić uwagę w przyszłości
- Szersza adopcja CI – W miarę jak więcej zespołów będzie integrować zestawy kontraktowe ze swoimi potokami, narzędzia prawdopodobnie staną się szybsze i bardziej konfigurowalne.
- Ustandaryzowane formaty kontraktów – Nowo powstające specyfikacje mogą ułatwić udostępnianie kontraktów między usługami i zespołami.
- Automatyzacja aktualizacji specyfikacji – Narzędzia, które wnioskują o kontraktach na podstawie zmian w kodzie, mogą zmniejszyć ciężar ręcznego utrzymania.
Jeśli zakładasz, że Twoje API jest solidne, ponieważ interfejs użytkownika działa płynnie, uruchomienie testu kontraktowego może ujawnić ukryte wady, które umykają testom manualnym. Dodanie wykonalnego kontraktu do potoku CI zmienia „wygląda dobrze” na „sprawdzone jako bezpieczne”.
Repozytorium: https://github.com/priya3054/zerodha-specmatic
