Czy faktycznie warto testować jednostkowo klienta API?

38

To mnie niepokoi od dłuższego czasu. Czy faktycznie warto testować jednostkowo klienta API?

Załóżmy, że tworzysz małą klasę, aby odciąć połączenia do interfejsu API REST sklepu zoologicznego. Petshop jest bardzo prostym API i ma podstawowy zestaw metod:

  • listProducts()
  • getProductDetails(ProductID)
  • addProduct(...)
  • removeProduct(ProductID)

Podczas testowania musielibyśmy stworzyć próbną usługę lub kpić z odpowiedzi. Ale to wydaje się przesadą; Rozumiem, że chcemy się upewnić, że nasze metody nie przestają działać z błędami literówek / składni, ale ponieważ piszemy funkcje wywołujące metody zdalne, a następnie tworzymy fałszywe odpowiedzi z tych metod zdalnych, wydaje się, że strata wysiłku i że testujemy coś, co tak naprawdę nie może zawieść. Co gorsza, jeśli zdalna metoda ulegnie zmianie, nasze testy jednostkowe zakończą się niepowodzeniem, gdy użycie produkcyjne się nie powiedzie.

Jestem prawie pewien, że coś mi umknęło, że mam niewłaściwy koniec kija lub nie widzę drewna na drzewa. Czy ktoś może ustawić mnie na dobrej drodze?

Phillip B Oldham
źródło
1
Gdyby nie był to tak prosty interfejs API z podstawowymi metodami, czy czułbyś się inaczej? Nawet szopa musi stawić czoła śniegowi.
JeffO,

Odpowiedzi:

31

Zadaniem zdalnego klienta API jest wykonywanie określonych połączeń - nie więcej, nie mniej. Dlatego jego test powinien zweryfikować, czy wykonuje te połączenia - nie więcej, nie mniej.

Jasne, że jeśli dostawca interfejsu API zmieni semantykę swoich odpowiedzi, wówczas system zawiedzie w produkcji. Ale to nie wina twojej klasy klienta; jest to coś, co można złapać tylko w testach integracyjnych. Opierając się na kodzie, który nie jest pod twoją kontrolą, zrezygnowałeś z możliwości weryfikacji poprawności za pomocą testów wewnętrznych - to była kompromis, a taka jest cena.

To powiedziawszy, testowanie klasy składającej się wyłącznie z delegacji do innej klasy może mieć niski priorytet, ponieważ ryzyko złożonych błędów jest stosunkowo niewielkie. Ale dotyczy to każdej klasy, która składa się tylko z jednolitych jednowarstwowych, nie ma to nic wspólnego z wywoływaniem kodu innego dostawcy.

Kilian Foth
źródło
Mmm, nie jestem pewien, czy się zgadzam. Możesz przetestować, które foo()zostanie wywołane wcześniej bar(), ale to nie znaczy, że dzwonienie foo()wcześniej bar()jest właściwą rzeczą; taki test jednostkowy byłby pozytywny, nawet jeśli kod jest niepoprawny. A jeśli to wszystko, co robi klient, konfigurowanie prób sprawdzających, czy ktoś foo()zostanie wcześniej wywołany, bar()jest stosunkowo kłopotliwe dla czegoś, co można zweryfikować pobieżnym spojrzeniem na kod klienta.
Doval
1
Możesz przetestować, czy add()metoda poprawnie dodaje dwie liczby, ale to nie znaczy, że dodawanie jest właściwą rzeczą do zrobienia w tym punkcie algorytmu - add()test jednostkowy powiódłby się, nawet jeśli twój program się pomylił. Jeśli jest to niewłaściwa rzecz, to twoja levenshteinDistance()metoda jest winna, a nie add()metoda. To jest dokładnie to samo. Kluczem do rozdzielenia kodu na metody jest zawsze to, że każda metoda musi dbać tylko o poprawienie jednej rzeczy.
Kilian Foth,
3
Teraz widzę, gdzie się nie zgadzamy! Jeśli polegasz na zewnętrznym sklepie zoologicznym, dla mnie oznacza to, że twój system kończy się na granicy HTTP, dlatego wydane wywołania REST wyjściami i podlegają testom. Jeśli weźmiesz pod uwagę ten moduł w sklepie zoologicznym, to tak, wzór wysłanych połączeń jest szczegółem implementacji, a test jednostkowy nie ma żadnego przepisu na ich temat.
Kilian Foth,
2
„Dlatego jego test powinien zweryfikować, czy wywołuje te połączenia”. Myślę, że takiej perspektywy nie dostrzegłem. Dzięki!
Phillip B Oldham
1
Na przykład mój test jednostkowy może sprawdzić, czy przy określonych parametrach treść żądania, które ma zostać wykonane, jest poprawna?
Maria Ines Parnisari
9

Krótka odpowiedź:

Wszystkie metody powinny być testowane jednostkowo.

Długa odpowiedź:

Tak. Jest tego warte.

Oto niektóre rzeczy, które powinny przetestować testy jednostkowe tych metod wywoływania API:

  • Że przekazujesz poprawnie utworzone lub poprawne parametry do wywołań API.
  • Że odpowiadasz odpowiednio na niektóre typy danych zwracane przez interfejsy API (wyśmiewane lub nie), na przykład może gdy interfejs API zwraca pusty ciąg, metoda powinna zwrócić wartość domyślną (tylko przykład)
  • Czy metody wywołujące zachowują się poprawnie, gdy wywołania API powodują błąd

Są to rzeczy, które robią wywoływane metody, które można odizolować, kpiąc sobie z usługi API i że poprzez ich dobre przetestowanie, zapewniamy, że błędy nie powstają w wyniku błędu w kodzie klienta, który wywołuje API.

Tulains Córdova
źródło
Mówisz „wyśmiewany czy nie” ... więc czy można testować na prawdziwym API? Czy mogę to nazwać testem integracyjnym, nawet jeśli wygląda na test jednostkowy? A może jest to inna nazwa? Chciałbym teście, że moje API wrapper robi to co mówi to robi, jakoś ...
Dan Rosenstark
1
@DanRosenstark Chyba w przypadku, gdy usługa API nie jest wyśmiewana, jest to test integracyjny.
Tulains Córdova
Czy nie wiedziałbyś za 5 sekund, czy odzyskujesz dane poprawnie podczas rzeczywistego wywołania interfejsu API? Ponieważ pozorowane interfejsy API nie są prawdziwymi wywołaniami, jedynym sposobem, by się nie powiodły, jest zmiana interfejsu API ... w takim przypadku testy próbne przejdą pomyślnie, ale prawdziwe wywołania zakończą się niepowodzeniem. Wydaje się bezcelowe
MattE
5

Nie byłyby to testy jednostkowe, ponieważ testujesz dane wejściowe i wyjściowe systemu, bardziej jak ograniczone testy integracyjne.

Bądź bardzo ostrożny, gdy mówisz „wydaje się to stratą wysiłku i że testujemy coś, co nie może naprawdę zawieść” - może zawieść, zawiedzie, prawdopodobnie zawiedzie w sposób, którego nie można przewidzieć, awarie będą gorsze, jeśli nie przeprowadzisz testów.

Błąd, który popełniacie tutaj, polega na wymyśleniu kół: wykonywanie połączeń do zdalnych usług i interfejsów API jest bardzo częstym scenariuszem, więc istnieje kilka całkiem dobrych narzędzi, które pomogą ci to przetestować. Ostatnim razem pracowałem nad aplikacją, która łączyła się ze zdalnymi usługami, korzystałem z SoapUIktóre mogą patrzeć na usługę i albo wykonywać fałszywe połączenia z tą usługą, albo zachowywać się jak lokalna kopia serwera, na której można wykonywać połączenia testowe i śledzić żądania i odpowiedzi. Konfiguracja zajęła kilka minut, a aktualizacja bardzo szybko przebiegała, jeśli zmienił się interfejs zdalny. Nie użyłem go w scenariuszu REST, ale nawet jeśli to nie działa dobrze (lub może ktoś czyta tę odpowiedź w przyszłości, gdy będą istniały lepsze narzędzia), powinieneś być w stanie znaleźć narzędzie, które może naśladować usługa dla Ciebie, a kiedy przyjdzie czas na wdrożenie kodu, będziesz zadowolony, że to zrobiłeś.

glenatron
źródło