Jak przetestować kod, który zależy od złożonych interfejsów API (na przykład Amazon S3)?

13

Mam problem z testowaniem metody, która przesyła dokumenty do Amazon S3, ale myślę, że to pytanie dotyczy każdej nietrywialnej zależności API / zewnętrznej. Wymyśliłem tylko trzy potencjalne rozwiązania, ale żadne nie wydaje się zadowalające:

  1. Uruchom kod, faktycznie prześlij dokument, sprawdź za pomocą interfejsu API AWS, czy został przesłany i usuń go na końcu testu. Spowoduje to, że test będzie bardzo wolny, będzie kosztował pieniądze za każdym razem, gdy zostanie przeprowadzony, i nie zawsze zwróci ten sam wynik.

  2. Makieta S3. Jest to bardzo owłosione, ponieważ nie mam pojęcia o wnętrznościach tego obiektu i wydaje się niewłaściwe, ponieważ jest zbyt skomplikowane.

  3. Upewnij się tylko, że MyObject.upload () jest wywoływany z właściwymi argumentami i ufaj, że poprawnie używam obiektu S3. Niepokoi mnie to, ponieważ nie ma sposobu, aby upewnić się, że poprawnie użyłem interfejsu API S3 z samych testów.

Sprawdziłem, jak Amazon testuje własny zestaw SDK, a oni robią sobie wszystko. Mają pomocnika o 200 liniach, który drwi. Nie wydaje mi się, żeby robienie tego samego było praktyczne.

Jak to rozwiązać?

sprężynowy
źródło
Nie odpowiedź, ale w praktyce stosujemy trzy podejścia, które ujawniłeś.
jlhonora

Odpowiedzi:

28

Istnieją dwa problemy, na które musimy się tutaj zwrócić.

Po pierwsze, wydaje się, że patrzysz na wszystkie swoje testy z perspektywy testu jednostkowego. Testy jednostkowe są niezwykle cenne, ale nie są jedynymi rodzajami testów. Testy można w rzeczywistości podzielić na kilka różnych warstw, od bardzo szybkich testów jednostkowych przez mniej szybkie testy integracyjne do jeszcze wolniejszych testów akceptacyjnych . (Można wyłamać jeszcze więcej warstw, np . Testy funkcjonalne ).

Po drugie, łączysz wywołania kodu zewnętrznego z logiką biznesową, co stwarza wyzwania testowe i może sprawić, że Twój kod będzie bardziej kruchy.

Testy jednostkowe powinny być szybkie i powinny być przeprowadzane często. Wyśmiewanie zależności pomaga w szybkim działaniu tych testów, ale może potencjalnie wprowadzić dziury w zasięgu, jeśli zależność się zmieni, a próbka się nie zmieni. Kod może zostać uszkodzony, gdy testy będą nadal działać na zielono. Niektóre kpiące biblioteki ostrzegają o zmianie interfejsu zależności, inne nie.

Z drugiej strony testy integracyjne służą do testowania interakcji między komponentami, w tym bibliotekami stron trzecich. Na tym poziomie testowania nie należy używać prób, ponieważ chcemy zobaczyć, jak rzeczywisty obiekt współdziała ze sobą. Ponieważ używamy prawdziwych obiektów, testy te będą wolniejsze i nie będziemy uruchamiać ich tak często, jak nasze testy jednostkowe.

Testy akceptacyjne wyglądają na jeszcze wyższym poziomie, sprawdzając, czy wymagania dotyczące oprogramowania są spełnione. Testy te działają na całym, kompletnym systemie, który zostałby wdrożony. Po raz kolejny nie należy używać kpiny.

Jedną z wytycznych, które ludzie uznali za wartościową w odniesieniu do prób, jest nie kpić z typów, których nie posiadasz . Amazon jest właścicielem interfejsu API do S3, dzięki czemu mogą się upewnić, że pod nimi się nie zmieni. Ty natomiast nie masz tych zapewnień. Dlatego jeśli wyśmiejesz interfejs S3 API w testach, może on zmienić i zepsuć kod, podczas gdy wszystkie testy będą miały kolor zielony. Jak więc przeprowadzamy test jednostkowy kodu, który korzysta z bibliotek stron trzecich?

Cóż, my nie. Postępując zgodnie z wytycznymi, nie możemy kpić z obiektów, których nie jesteśmy właścicielami. Ale… jeśli posiadamy nasze bezpośrednie zależności, możemy wyśmiewać je. Ale jak? Tworzymy własne opakowanie dla interfejsu API S3. Możemy sprawić, że będzie wyglądać podobnie do S3 API, lub możemy dostosować go do naszych potrzeb (preferowane). Możemy nawet uczynić to trochę bardziej abstrakcyjnym, powiedz PersistenceServiceraczej niż an AmazonS3Bucket. PersistenceServicebyłby interfejs z metodami takimi jak #save(Thing)i #fetch(ThingId), rodzajami metod, które chcielibyśmy zobaczyć (są to przykłady, możesz chcieć różnych metod). Możemy teraz zaimplementować PersistenceServiceinterfejs API S3 (powiedzmy a S3PersistenceService), odsuwając go od naszego kodu wywołującego.

Teraz kod, który wywołuje interfejs API S3. Musimy zastąpić te wywołania wywołaniami do PersistenceServiceobiektu. Używamy zastrzyku zależności, aby przekazać nasz PersistenceServiceobiekt. Ważne jest, aby nie prosić o S3PersistenceService, ale prosić o PersistenceService. To pozwala nam zamienić implementację podczas naszych testów.

Cały kod, który używał bezpośrednio interfejsu API S3, teraz używa naszego PersistenceService, a S3PersistenceServiceteraz wykonuje wszystkie wywołania interfejsu API S3. W naszych testach możemy wykpić się PersistenceService, ponieważ jesteśmy jego właścicielem, i użyć makiety, aby upewnić się, że nasz kod wykonuje prawidłowe wywołania. Ale teraz to pozostawia sposób testowania S3PersistenceService. Ma taki sam problem jak poprzednio: nie możemy przetestować urządzenia bez wezwania zewnętrznego serwisu. Więc… nie testujemy tego jednostkowo. My mogli naśmiewać się z zależnościami S3 API, ale to dałoby nam trochę do żadnej dodatkowej pewności siebie. Zamiast tego musimy przetestować to na wyższym poziomie: testy integracyjne.

Może to zabrzmieć trochę niepokojąco, mówiąc, że nie powinniśmy testować jednostkowo części naszego kodu, ale spójrzmy na to, co osiągnęliśmy. Wszędzie mieliśmy mnóstwo kodu, w którym nie mogliśmy przeprowadzić testu jednostkowego, który teraz można przetestować za pomocą PersistenceService. Mamy bałagan w bibliotece stron trzecich ograniczony do jednej klasy implementacji. Ta klasa powinna zapewniać niezbędną funkcjonalność do korzystania z interfejsu API, ale nie jest do niego dołączona żadna zewnętrzna logika biznesowa. Dlatego po napisaniu powinien być bardzo stabilny i nie powinien się bardzo zmieniać. Możemy polegać na wolniejszych testach, które nie uruchamiamy tak często, ponieważ kod jest stabilny.

Następnym krokiem jest napisanie testów integracji S3PersistenceService. Powinny być one oddzielone według nazwy lub folderu, abyśmy mogli je uruchomić oddzielnie od naszych szybkich testów jednostkowych. Testy integracyjne mogą często wykorzystywać te same ramy testowe, co testy jednostkowe, jeśli kod jest wystarczająco informacyjny, więc nie musimy uczyć się nowego narzędzia. Rzeczywisty kod do testu integracji jest tym, co napiszesz dla Opcji 1.

Cbojar
źródło
pytanie brzmi: w jaki sposób przeprowadzasz testy integracji, a raczej testy e2e dla interfejsu API, który udostępniasz. Nie można kpić z usługi PersistenceService z oczywistych powodów. Albo coś źle zrozumiałem, albo dodałem kolejną warstwę pomiędzy API aplikacji i AWS API, nie daje nic więcej niż łatwiejszy czas na przeprowadzanie testów jednostkowych
Yerken
@ Yerken Kiedy się nad tym zastanawiam, jestem prawie pewien, że mógłbym wypełnić kolejną długą odpowiedź na to pytanie. To może nawet być dla ciebie opłacalne, ponieważ możesz uzyskać coś więcej niż tylko moją odpowiedź.
cbojar
4

Musisz zrobić jedno i drugie.

Uruchamianie, przesyłanie i usuwanie jest testem integracyjnym. Łączy się z systemem zewnętrznym i dlatego można oczekiwać, że będzie działał wolno. Prawdopodobnie nie powinien być częścią każdej kompilacji wykonywanej lokalnie, ale powinien być częścią kompilacji CI lub kompilacji nocnej. To równoważy powolność tych testów i nadal zapewnia wartość automatycznego testowania.

Potrzebujesz także unittestów, które działają szybciej. Ponieważ ogólnie rzecz biorąc rozsądnie jest nie polegać zbytnio na systemie zewnętrznym (abyś mógł zamienić implementacje lub przełączyć się), prawdopodobnie powinieneś spróbować napisać prosty interfejs na S3, na którym można kodować. Wyśmiewaj ten interfejs w unittests, abyś mógł szybko uruchamiać unittests.

Pierwsze testy sprawdzają, czy kod rzeczywiście działa z S3, drugie testy, w których kod poprawnie wywołuje kod, który komunikuje się z S3.

JDT
źródło
2

Powiedziałbym, że zależy to od złożoności korzystania z interfejsu API .

  1. Zdecydowanie musisz wykonać przynajmniej kilka testów, które faktycznie wywołują interfejs API S3 i potwierdzają, że działał on od końca do końca.

  2. Zdecydowanie musisz także przeprowadzić dodatkowe testy, które tak naprawdę nie wywołują interfejsu API, abyś mógł odpowiednio przetestować własne oprogramowanie bez wywoływania interfejsu API przez cały czas.

Pozostaje pytanie: czy musisz wyśmiewać interfejs API?

Myślę, że to zależy od tego, jak dużo z tym zrobisz. Jeśli wykonujesz tylko jedną lub dwie proste czynności, nie sądzę, abyś musiał zadawać sobie tyle trudu, co makieta. Byłbym zadowolony po prostu sprawdzając, czy korzystam z funkcji i przeprowadzając testy na żywo.

Jeśli jednak korzystanie z niego jest bardziej złożone, z różnymi scenariuszami i różnymi zmiennymi, które mogą wpływać na wyniki, prawdopodobnie trzeba go wyszydzić, aby przeprowadzić dokładniejsze testy.


źródło
1

Dodając do poprzednich odpowiedzi, głównym pytaniem jest, czy (i jak) chcesz wyśmiewać interfejs API S3 do swoich testów.

Zamiast ręcznie wyśmiewać poszczególne odpowiedzi S3, możesz skorzystać z niektórych bardzo wyrafinowanych istniejących frameworków. Na przykład moto zapewnia funkcjonalność, która jest bardzo podobna do rzeczywistego API S3.

Możesz także spojrzeć na LocalStack , platformę, która łączy istniejące narzędzia i zapewnia w pełni funkcjonalne środowisko chmury lokalnej (w tym S3), które ułatwia testowanie integracji.

Chociaż niektóre z tych narzędzi są napisane w innych językach (Python), powinno być łatwo rozdzielić środowisko testowe w zewnętrznym procesie na podstawie testów, powiedzmy, Java / JUnit.

wicher
źródło