Jak testujesz funkcję, której jedynym celem jest zapytanie zewnętrznego interfejsu API, ale interfejs API używa złożonej składni zapytania?

16

Jedyną prawdziwą logiką jest składnia zapytania dla zewnętrznego interfejsu API. Nie chcę testować, czy wysyła zapytanie do interfejsu API, chcę przetestować, czy wysyła zapytanie w taki sposób, aby zwrócone zostały prawidłowe dane. Na przykład jakiś pseudo-kod:

function retrieve_related_data(id)
{
  query = "[potentially long, syntactically complex query that
            uses param id to get some data]";
  results = api_wrapper.query(query);
  return results;
}

Bardziej konkretny przykład z gotowym interfejsem API:

function retrieveLifeSupportingObjectsWithinRegion(id)
{
  query = "
    within region(" + id + ") as r
    find objects matching hydration>0 and temp_range has 75
    send name, id, relative(position, r)        
  ";
  results = astronomicalObjectApiWrapper.query(query);
  return results;
}

Kwerenda ma niestandardową składnię interfejsu API i jest złożona oraz istnieje wiele sposobów na uzyskanie takich samych lub podobnych wyników. Celem tej funkcji nie jest uzyskanie danych identyfikowanych przez, idale znalezienie podzbioru innych danych opartych na rozmytej relacji z danymi identyfikowanymi przez to, idktóry spełnia również kilka innych wymagań. Pozostałe wymagania są zawsze takie same niezależnie od, idale mogą ulec zmianie w miarę modyfikowania systemu. Na przykład, jeśli przykładowy interfejs API dodał obsługę informacji o grawitacji, możemy chcieć zmienić zapytanie, aby użyć grawitacji również do udoskonalenia wyników. A może wymyślimy bardziej skuteczny sposób sprawdzenia zakresu temperatur, ale to nie zmienia wyników.

Chcę przetestować, że dla danego wejścia idzwracany jest prawidłowy zestaw danych. Chcę to przetestować, aby jeśli ktoś pomieszał kwerendę w taki sposób, że nie zwraca już poprawnych danych w związku z idtym, że zakończy się ona niepowodzeniem, ale chcę również, aby ludzie mogli modyfikować zapytanie w celu dopracowania go bez konieczności modyfikacji test.

Opcje, które rozważałem:

  1. Mógłbym usunąć interfejs API, ale byłoby to albo zbyt proste (sprawdź, czy idjest ono obecne w zapytaniu, a następnie zwrócił oczekiwany zestaw danych, jeśli jest, lub nieoczekiwany zestaw, jeśli nie jest), zbyt kruche (sprawdź, czy ciąg zapytania jest dokładnie to, co znajduje się w funkcji) lub zbyt skomplikowane (sprawdź, czy użyte zapytanie jest poprawne pod względem składniowym i spowoduje zwrócenie poprawnych danych).

  2. Mógłbym przesłać zapytanie do prawdziwego interfejsu API, ale oczekiwane wyniki mogą się zmieniać w czasie wraz ze zmianami danych w systemie zewnętrznym, poza kontrolą systemu testowego.

  3. Mógłbym spojrzeć na konfigurację instalacji prawdziwego interfejsu API w celu kontrolowania posiadanych danych, ale to jest duży wysiłek.

Opieram się na # 2 i robię to raczej test integracyjny, który nie jest uruchamiany często, i widzę, jak często zmiany w danych systemu zewnętrznego powodują przerwanie testu. Myślę, że na razie byłoby to najprostsze, ale zastanawiam się, czy istnieją alternatywy, o których nie myślę, ani lepsze sposoby rozwiązania tego problemu. Wszelkie porady będą mile widziane.

Joshua Coady
źródło
Myślałem o tym jako o teście jednostkowym, ale może to naprawdę test integracyjny lub test akceptacyjny na niskim poziomie?
Joshua Coady
2
Czy to interfejs API tylko do odczytu? Czy jesteś w stanie zapisać dane, na podstawie których możesz wiarygodnie zweryfikować swoje odczyty ?
svidgen
Czy to pytanie różni się od „jak sprawdzić, czy moja sql (= złożona składnia) zwraca poprawne dane”? W bazach danych zwykle masz kilka testów integracyjnych, które testują składnię crud-sql i fałszywą repozytorium fasady w celu weryfikacji businesslogic
k3b

Odpowiedzi:

7

Może się wydawać, że sprawdzając zewnętrzną odpowiedź API, testowalibyśmy naszą funkcję, ale nie byłoby to do końca prawdą. W jakiś sposób testowalibyśmy zewnętrzny interfejs API i środowisko, w którym działa interfejs API.

Nasze testy powinny zostać zaadresowane, aby zagwarantować oczekiwane zachowanie napisanego przez nas kodu, a nie kodu napisanego przez strony trzecie.

Do pewnego stopnia musimy ufać właściwej funkcji interfejsów API i bibliotek, na których polegamy. Na przykład zwykle nie testujemy implementowanych komponentów środowiska.

Dlaczego tak mówię?

Chcę przetestować, że dla danego identyfikatora wejściowego zwracany jest prawidłowy zestaw danych

Co by tu przetestowano? Jak powiedziałeś, dane i ich poprawność nie są pod naszą kontrolą, dlatego ograniczamy sukces fazy testowania do zewnętrznego agenta, na który nie mamy żadnej kontroli. Testy te są kandydatami, aby stać się niedeterministycznymi i ostatecznie, nie chcemy tego rodzaju testów w naszym procesie budowy .

Inną kwestią jest zatwierdzenie umowy. Uważam, że bardzo przydatny jest test umowy 1, aby upewnić się, że integracja nadal działa zgodnie z oczekiwaniami, przed wydaniem lub wdrożeniem.

Chcę to przetestować, aby jeśli ktoś pomieszał zapytanie w taki sposób, że nie zwraca już poprawnych danych na podstawie identyfikatora, że ​​zakończy się niepowodzeniem

Co jeśli zapytanie jest prawidłowe, ale dane są nieprawidłowe z powodu błędów w interfejsie API? Nie tylko dane są poza naszą kontrolą. Logika też jest.

Pomóc może w tym wdrożenie testów funkcjonalnych lub kompleksowych testów. Możesz rozwiązać ten test, aby sprawdzić poprawność określonych ścieżek wykonania, aby jeśli API zwróciły nieprawidłowe dane, prawdopodobnie spowodowałoby to nieoczekiwane zachowanie i wyniki. Z drugiej strony spodziewałbym się, że interfejs API zgłasza błędy, jeśli moje zapytania były źle sformatowane.

Ale chcę też, aby ludzie mogli modyfikować zapytanie w celu dopracowania go bez potrzeby modyfikowania testu.

Sugeruję wdrożenie narzędzia do tego celu. Może to być tak proste, jak:

  • Klasa, która działa jako test, ale nie należy do planu testów
  • Skrypt powłoki + curl

Lub coś bardziej wyrafinowanego. Na przykład samodzielny klient.

W każdym razie funkcja pod pytaniem, warta dwóch rodzajów testów:

  • Test jednostkowy. Jak już powiedziałeś, musisz zetrzeć zewnętrzny interfejs API, ale taki jest cel testów jednostkowych. Testowanie naszego kodu izolującego zależności.

  • Test integracyjny. Sprawdź, czy kod nie tylko wysyła prawidłowe żądanie, ale także odpowiednio obsługuje treść odpowiedzi, błędy, przekierowania itp. Wykonaj testy dla wszystkich tych przypadków, ale nie testuj danych .

Uwaga dodatkowa: Twoje pytanie jest podobne do pytania: w jaki sposób testujesz instrukcje SQL aplikacji?

Powiązane pytania :


1: Być może zainteresuje Cię odpowiedź @ DocBrown na ten temat

Laiv
źródło
„Problem polega na tym, że zbytnio skupiasz się na testowaniu zewnętrznego interfejsu API”. - Nic nie wskazuje na to, że pytający jest zainteresowany testowaniem zewnętrznego API. Mówisz także „stub the external API”, ale czy masz jakieś sugestie, czy pytający powinien użyć opcji „zbyt prosty”, opcji „zbyt kruchy”, opcji „zbyt skomplikowany”, czy też jakiejś czwartej opcji?
Tanner Swett
Pytanie OP dotyczy sposobu testowania funkcji wywołującej zewnętrzny interfejs API. Ale czytając jego wątpliwości, wydaje mi się, że przykłada zbyt dużą wagę do testowania zapytania i jego wyników. Zrobiłem 4 sugestie: (1) nie wykonuj testu interfejsu API. (2) nie używaj testów integracji jako środowiska roboczego do dostrajania zapytania. Zamiast tego stwórz narzędzie. (3) wracając do głównego pytania, wykonaj test jednostkowy i integracyjny. Ale nie sprawdzanie poprawności treści odpowiedzi API. (4) Zapytaj kierownika projektu, czy powinien / może wykonać zestaw testów zewnętrznego interfejsu API jako część planu testów projektu.
Laiv
1
Samo zapytanie traktuję jako „kod napisany przez nas”. Celem końcowym jest automatyczne testowanie w celu powiadomienia nas, jeśli wprowadzimy błąd w naszym kodzie. Odnosząc się do tego, co powiedziałeś o instrukcjach SQL, myślę, że jest to podobne do tego - wydaje mi się, że moje pytanie jest jak w jaki sposób testujesz, że twój kod wysyła zapytania do zewnętrznego API w sposób, w jaki API zareaguje zgodnie z przeznaczeniem (zakładając, że odpowiedź nominalna). Myślę, że mówisz o pozostawieniu tego poza testami jednostkowymi i integracyjnymi, ale jeśli zapytania mają kluczowe znaczenie dla misji, moglibyśmy skonfigurować inne automatyczne testy osobno, aby przetestować zewnętrzny interfejs API na żywo.
Joshua Coady
1
IMO, najlepszym sposobem jest przeprowadzenie testu funkcjonalnego, zmiana klauzuli where, która nigdy nie będzie prawdziwa, spowoduje inne zachowanie w jednym lub kilku moich testach funkcjonalnych. UT to tylko niewielka część planu testowego.
Laiv
2
Dzięki za bycie radą. Wydaje mi się, że ostatecznie, mimo że zapytanie zawiera logikę niestandardową, jest ono poza domeną testów jednostkowych, ponieważ zapytanie jest kodem „wykonywanym” poza testowanym systemem. Po prostu potrzebowałem kogoś, kto powiedziałby mi to wiele razy na różne sposoby, zanim to zobaczyłem;)
Joshua Coady
2

Widziałem kontrole jednostek, które sprawdzają, czy wygenerowany ciąg zapytania odpowiada oczekiwanej wartości.

Jednak. Moim zdaniem było to ograniczone użycie. Składnia zapytania była skomplikowana, prawdopodobnie błędna, więc A istniały nieskończone możliwości sprawdzania, a B, nawet jeśli łańcuch został „poprawnie” wygenerowany, nieoczekiwane wyniki można było zwrócić w środowisku na żywo.

Myślę, że masz rację, wybierając opcję 2. uruchom testy integracji z instancją na żywo.

Dopóki nie są destrukcyjne, są to pierwsze testy, które powinieneś napisać, ponieważ wychwytują, ale nie identyfikują przyczyny, żadnego błędu.

Przejście na opcję 3 „wdrożenie instancji testowej z danymi zastępczymi” jest lepsze. Nie ma to jednak wpływu na pisanie testów, ponieważ możesz skierować te same testy na serwer testowy, jeśli i kiedy będzie to dobre wykorzystanie czasu na jego wdrożenie.

Ewan
źródło
0

Zależy to od interfejsu API, ale jeśli to możliwe, wybierz opcję nr 3 (prywatna instancja testowa).

Stubbing API (opcja nr 1) jest najgorszą opcją z powodów, o których wspomniałeś, a wybranie tej trasy prawdopodobnie wyrządzi więcej szkody niż pożytku (dużo straconego czasu).

Uruchamianie z prawdziwym API (opcja 2) powoduje, że testy są niestabilne i niewiarygodne, a po kilku fałszywych trafieniach ludzie przestaną ich używać. Dane mogą nie tylko ulec zmianie, ale usługa może również nie działać. Moim zdaniem jest to podobne do braku testów zapytań i polegania na testach integracyjnych / systemowych w celu znalezienia problemów. To powiedziawszy, jeśli dane API rzadko się zmieniają, a sam interfejs API prawie zawsze działa, może to być opłacalna opcja. Większość interfejsów API nie pasuje do tego opisu.

Ostatecznie sprowadza się to do tego, jak ważne i złożone są te zapytania: jeśli jest ich więcej niż kilka, a niektóre z nich są na tyle złożone, że odczuwasz potrzebę ich przetestowania, zainwestowałbym w utworzenie prywatnej instancji do testowania . Opłaci się tak jak inne testy jednostkowe.

Michał Tenenberg
źródło
Więc w zasadzie mówisz, że testy jednostkowe (nr 1) i testy integracyjne (nr 2) są szkodliwe? Chociaż # 3 może wydawać się najlepszym spośród nich, może być również najdroższy. Należy go utrzymywać przy każdej zmianie interfejsu API. Bez # 2 nie będziesz świadomy ewentualnych zmian błędów w prawdziwym API, dopóki twoja aplikacja nie będzie w produkcji (za późno na podjęcie działań). Ok, nr 1 wydaje się niepotrzebny, ponieważ nie ma linii kodu do przetestowania ... dziś ... jutro, skąd to wie ...
Laiv
Mówię, że złe testy są zdecydowanie szkodliwe. Testy niestabilne marnują dużo czasu i wysiłku i powodują, że ludzie tracą wiarę w testy jednostkowe jako całość. Testy, które psują się wraz ze zmianami implementacji (nr 1) lub po prostu losowo, gdy zmiany danych (nr 2) nie są dobrymi testami.
Michał Tenenberg,
Test integracji nie testuje danych. Otóż ​​to. Nie mogą przerwać testowania, wystarczy zweryfikować integrację. Testowanie nie jest kwestią wiary, ale kwestią dobrych nawyków, które zwiększają wartość aplikacji
Laiv