Testowanie klienta REST na serwerze REST. Jak robić mecze?

10

Pisząc testy jednostkowe, często używa się urządzeń: mało danych do przetestowania, więc możemy powiedzieć: 1. Zdobądź wszystkich klientów, którzy powinni dołączyć Willy Wonka. 2. Usuń klienta 3, a teraz zdobądź klientów nie powinien już zawierać Willy Wonka.

To dobrze w testach jednostkowych. Użyj setup / teardown, aby ponownie załadować urządzenia lub wycofać transakcję. Testowanie tworzy, aktualizuje i usuwa w ramach transakcji . Nowe dane tymczasowe trwają tylko przez czas trwania tego testu, a następnie są resetowane.

A co z oddzieleniem serwera REST od klienta REST?

Chcemy mieć pewność, że nasz klient REST nie tylko poprawnie odczytuje, ale również tworzy, aktualizuje i usuwa poprawnie.

Nie udało mi się znaleźć żadnych przykładów ani sugestii, jak to zrobić w przypadku zdalnego testowego serwera REST.

Zakładając, że mam testowy serwer REST obsługujący tylko urządzenia. Cały bezpaństwowy charakter HTTP oznacza, że ​​trudno byłoby wysłać komunikat „POCZĄTEK TRANSAKCJI” i „TRANSAKCJA ROLLBACK” lub „RELOAD FIXTURES”, prawda?

Nie mogę być pierwszym, który chce to zrobić, więc mam wrażenie, że potrzebuję innego sposobu myślenia o tym.

Jakieś sugestie?

sivers
źródło
Być może, ponieważ jest to serwer testowy, możesz mieć punkt końcowy, który przeładuje urządzenia?
David Radcliffe,
Jeśli Twoim głównym problemem jest przywrócenie serwera testowego do predefiniowanego stanu, dlaczego nie dodasz specjalnych funkcji testujących, takich jak „RELOAD TESTDATA” do pozostałego interfejsu API, aby robić to, co chcesz? Oczywiście powinieneś się upewnić, że tego rodzaju wywołania API nie są dostępne w wersji produkcyjnej.
Doc Brown

Odpowiedzi:

7

Systemy oprogramowania idealnie mają dobrze zdefiniowane granice systemu i interfejsy między nimi. Usługi REST są tego dobrym przykładem.

W tym celu, polecam przeciwko temu, co próbujesz zrobić.

Konkretnie:

Chcemy mieć pewność, że nasz klient REST nie tylko poprawnie odczytuje, ale również tworzy, aktualizuje i usuwa poprawnie.

Zamiast tego sugerowałbym:

  • Budowanie testów dla klienta REST, aby upewnić się, że zachowuje się on poprawnie, biorąc pod uwagę określone dane wejściowe i wyjściowe. Uwzględnij dobre (oczekiwane) i złe (nieoczekiwane) wartości.

  • Budowanie testów dla usługi REST (jeśli ją kontrolujesz), aby zachowywać się zgodnie z jej zamierzoną funkcją

  • Testy powinny znajdować się blisko ich problematycznej dziedziny, aby pomóc w projektowaniu i rozwijaniu tego, co ważne w tym kontekście

briandoll
źródło
3
Odrzucasz cały pomysł testów integracyjnych tutaj całkiem przypadkowo. Nie sądzę, że takie podejście jest oparte na praktyce.
luty
Dziękuję za wszystkie pomocne sugestie. Również przez Twitter otrzymałem świetne sugestie, aby wypróbować klejnot Ruby „webmock” i podobne do kpienia z odpowiedzi serwera API REST. Zgadzam się również z „febelingiem”, że to, co opisuję, wydaje się być raczej testem integracyjnym, więc przyjrzę się temu osobno. Jeszcze raz dziękuję wszystkim. - Derek
sivers
wyśmiewanie API to świetny sposób na rozwiązanie problemu. Ale jak się upewnić, że wyśmiewany API == prawdziwy API?
FrEaKmAn
4

Pamiętaj o dwóch kątach:

  • Czy testujesz swój kod lub hydraulikę? Zakładając, że korzystasz z dobrze znanej usługi i stosu klientów, prawdopodobnie możesz bezpiecznie założyć ich testerów, a tysiące użytkowników na ogół upewnią się, że nie ma podstawowego błędu w podstawach.
  • Dlaczego twoje testy nie są idempotentne? Zrób sposób na zapisywanie danych nieprodukcyjnych lub zapisywanie w innym punkcie końcowym. Wybierz przewidywalny wzór nazewnictwa. Wstępnie załaduj resztę bazy danych serwera przed testami. I prawdopodobnie jest kilka innych sposobów, aby tak się stało - metoda jest naprawdę taktyczna i powinna zależeć od charakteru aplikacji.
Wyatt Barnett
źródło
1

Jak powiedzieli inni, jeśli testujesz klienta, nie musisz sięgać aż do tworzenia, usuwania itp. Na serwerze. Często nie musisz nawet kpić z serwera. Naprawdę musisz tylko upewnić się, że wysyłasz właściwe żądania i poprawnie obsługujesz odpowiedzi, bez względu na to, czy jest napisane w Ruby, Python, PHP lub cokolwiek innego, w pewnym momencie twój klient prawdopodobnie użyje metody z biblioteki HTTP, aby zrobić żądanie i wystarczy wykpić tę metodę, sprawdzić, jak się nazywa, i zwrócić wynik testu.

Weźmy hipotetycznego klienta Python, który używa urllib2do wykonywania żądań. Prawdopodobnie masz jakąś metodę w kliencie, nazwijmy ją get(), która ma w sobie wywołanie urllib2.Request(). Naprawdę wystarczy kpić z połączenia z własną klasą get().

@patch('your.Client.get')
def test_with_mock(self, your_mock):
    your_mock.return_value({'some': 'json'})
    test_obj = your.Client.get_object(5)
    your_mock.assert_called_with('/the/correct/endpoint/5')

Ten bardzo uproszczony przykład wykorzystuje bibliotekę Mock Pythona do testowania hipotetycznej your.Clientklasy za pomocą get_object()metody, która generuje poprawny adres URL, aby uzyskać coś z jakiegoś API. Aby złożyć żądanie, klient wywołuje get()metodę z tym adresem URL. W tym przypadku metoda ta jest wyśmiewana ( your.Client.get„łatana”, aby była pod kontrolą your_mock), a test sprawdza, czy zażądano właściwego punktu końcowego.

Wyśmiewana metoda zwraca skonfigurowaną odpowiedź JSON ( your_mock.return_value), którą klient musi obsłużyć, a użytkownik wykonałby dalsze stwierdzenia, aby sprawdzić, czy obsłużył oczekiwane dane w oczekiwany sposób.

Sean Redmond
źródło
Ale skąd masz pewność, że kiedy wysyłasz „właściwe” żądanie, że jest to rzeczywiste żądanie (w produkcji)? Ponieważ jeśli zrozumiem twoją sugestię, jeśli interfejs API ulegnie zmianie lub się zepsuje, twoje testy będą nadal działać. Podczas produkcji jest to zupełnie inna historia.
FrEaKmAn
1

Opisujesz scenariusz testu integracji. Zazwyczaj są one nieco niewygodne w konfiguracji i burzeniu. Powoduje to, że biegają powoli i często są kruche.

Podejście do urządzeń jest równie niezręczne i niezdarne, ale jest to domyślny sposób, w jaki radzą sobie z tym niektóre frameworki, np. Rails, i jest już obsługiwany. Potrzebują abstrakcyjnego przypadku testowego lub czegoś podobnego, aby przygotować bazę danych z urządzeniami. (Uwaga na nietypowe nazewnictwo kategorii testowych w Railsach, testy jednostkowe z urządzeniami DB są ściśle mówiąc również testami integracyjnymi.)

Chciałbym zająć się twoim scenariuszem, aby zaakceptować specyficzną dla testu kontrolę nad stanem aplikacji API lub jej bazą danych. Możesz mieć dodatkowe punkty końcowe do instalacji i porzucenia, które są obecne tylko w środowisku testowym. Lub alternatywnie, rozmawiasz z bazą danych (lub czymkolwiek, czego używasz) z tyłu aplikacji / interfejsu API.

Jeśli uważasz, że to za dużo (w sensie nadmiernego) wysiłku, zastanów się, że podejście z urządzeniami do baz danych robi to samo: używając dodatkowych, specyficznych dla testu środków do manipulowania stanem bazy danych lub aplikacji.

Nie sądzę jednak, aby ta dyskusja miała związek z bezstanową naturą HTTP. HTTP jest bezstanowy, ale aplikacja zdecydowanie nie jest w większości przypadków. To brzmi trochę tak, jakbyś szukał surowości REST. Równie dobrze możesz mieć wszystkie zasoby, które można w pełni tworzyć, odczytywać i usuwać. W takim przypadku możesz po prostu przeprowadzić konfigurację i porzucić za pomocą zwykłych środków API. Często jednak nie robi się tego w praktyce, ponieważ nie chcesz uwzględniać niektórych operacji w biznesowym rozumieniu twojej aplikacji, przynajmniej poza środowiskiem testowym.

febeling
źródło
1

Łatka Małpy

W mojej pracy wykonujemy ATDD przy użyciu wychodzącego frameworka xUnit i łatania połączeń sieciowych między klientem a serwerem. W tej samej przestrzeni procesu ładujemy klienta, małpujemy łatanie połączenia sieciowego na górze kodu stosu serwera REST. Wszystkie połączenia są następnie wysyłane przez klienta, tak jak normalnie, a kod serwera otrzymuje żądania dokładnie tak, jak normalnie by się pojawiały.

Korzyści

  • bez konieczności synchronizacji z uruchomieniem serwera (ponieważ nie ma serwera)
  • wykorzystaj klasyczną konfigurację jednostek i metodę porzucenia, aby zarządzać takimi urządzeniami, jak urządzenia
  • umiejętność używania makiet / odcinków i innych łat małp w celu dokładniejszego kontrolowania testu
  • można napisać przy użyciu frameworka xUnit

Kompromisy

  • nie ujawnia interakcji / problemów wieloprocesowych (blokowanie, głód zasobów itp.)
  • nie ujawnia problemów z wieloma serwerami (serializacja danych, styl klastrowania)
  • nie ujawnia problemów z siecią, ponieważ jest to symulowane (błędy dostępu, błędy przekroczenia czasu itp.)
dietbuddha
źródło