Jak mogę przetestować klasę, która wymaga połączenia z serwisem internetowym?

21

Próbuję przetestować klasę, która wywołuje niektóre usługi sieciowe Hadoop. Kod ma prawie postać:

method() {
    ...use Jersey client to create WebResource...
    ...make request...
    ...do something with response...
}

np. istnieje metoda tworzenia katalogów, metoda tworzenia folderów itp.

Biorąc pod uwagę, że kod dotyczy zewnętrznej usługi internetowej, nad którą nie mam kontroli, jak mogę to przetestować? Mógłbym próbować kpić z klienta / odpowiedzi usługi sieci Web, ale to łamie wskazówkę, którą ostatnio często widziałem: „Nie kpij z obiektów, których nie posiadasz”. Mógłbym skonfigurować fałszywe wdrożenie usługi internetowej - czy nadal stanowiłoby to „test jednostkowy”, czy też byłby to test integracji? Czy po prostu nie jest możliwe przeprowadzenie testu jednostkowego na tak niskim poziomie - jak by to zrobił specjalista TDD?

Chris Cooper
źródło
5
Gdzie widziałeś wskazówki, jak nie kpić z rzeczy, których nie posiadasz? To wydaje się być ogromnym powodem, dla którego powinieneś kpić z rzeczy ...
Thomas Owens
1
Przeczytałem go na wielu blogach, z których jeden odwołuje się do amazon.com/Growing-Object-Oriented-Software-Guided-Tests/dp/..., który, jak wiem, jest dobrze ocenianą książką ( blog.8thlight. com / eric-smith / 2011/10/27 / thats-not-yours.html , mockobjects.com/2007/04/test-smell-everything-is-mocked.html )
Chris Cooper
1
@ChrisCooper: Chciałbym zauważyć, że ostatni link jest bardzo nieaktualny (od 2007 r.). Od tego czasu wiele się zmieniło. Czuję na podstawie postu, że szydzenie było wtedy znacznie trudniejsze, kiedy trzeba było faktycznie zaimplementować zachowanie, a nie po prostu użyć frameworku lub metaprogramowania, aby ustawić zwracane wartości ...
c_maker

Odpowiedzi:

41

Moim zdaniem powinieneś wyśmiewać wywołania usługi internetowej, jeśli jest to test jednostkowy, a nie test integracyjny.

Twój test jednostkowy nie powinien sprawdzać, czy zewnętrzna usługa internetowa działa lub czy twoja integracja z nią jest prawidłowa. Nie przesadzając z dogmatycznym podejściem do TDD, zauważ, że efektem ubocznym przekształcenia testu jednostkowego w test integracyjny jest to, że może on działać wolniej i potrzebujesz szybkich testów jednostkowych.

Ponadto, jeśli usługa tymczasowo nie działa lub działa nieprawidłowo, czy powinno to spowodować niepowodzenie testu urządzenia? To nie wydaje się właściwe. Twój test jednostkowy powinien zakończyć się niepowodzeniem tylko z jednego powodu: jeśli w kodzie w tej „jednostce” występuje błąd.

Jedyną istotną częścią kodu jest tutaj ...do something with response.... Kpij z reszty.

Andres F.
źródło
2
Pamiętaj, że będziesz musiał utrzymywać próbny podpis obiektu i zwracać wartości w synchronizacji z wartościami generowanymi przez usługę internetową Hadoop podczas nieuniknionych zmian w tej usłudze.
pcurry 11.10.2013
Rzadko warto testować gotowe elementy, takie jak Hadoop. Ale jeśli dzwonisz na niestandardową usługę internetową świadczoną przez inny zespół lub organizację, możesz napisać dla niej test w samoobronie. W ten sposób, gdy coś pójdzie nie tak, możesz szybko sprawdzić, czy problem tkwi w kodzie, czy w serwisie internetowym. To nie jest test jednostkowy uruchamiany automatycznie; jest to diagnostyka uruchamiana w razie potrzeby.
kevin cline
@kevincline W pełni zgadzam się co do konieczności proponowanych przez ciebie testów i rzeczywiście piszę je w mojej codziennej pracy i okazały się przydatne. Ale z definicji NIE są to testy jednostkowe, o co chodziło w tym pytaniu :) Zastanów się: jeśli jest to test jednostkowy, a kod się nie powiedzie, ponieważ usługa internetowa została zmieniona, jaka jest „jednostka”, którą testujesz? Co dokładnie zawiodło? Nie testujesz w izolacji, zgodnie z potrzebami testów jednostkowych.
Andres F.
1
@AndresF .: Myślę, że jesteśmy w brutalnej zgodzie: „To [diagnostyka] NIE jest testem jednostkowym ...”
Kevin Cline
@kevincline Right! Przepraszam, źle odczytałem twój komentarz!
Andres F.,
5

Nie zgadzam się z „nie kpij sobie z obiektów, których nie posiadasz”, gdy przeprowadzasz testy jednostkowe.

Pozornym celem istnienia jest fakt, że będą moduły, biblioteki, klasy, których nie będziemy posiadać.

Moją propozycją dla twojego scenariusza jest udawanie połączenia z serwisem internetowym.

Skonfiguruj próbkę w taki sposób, aby zwracała dane z powrotem do modułu.
Upewnij się, że uwzględniono wszystkie scenariusze, np. Gdy zwracane dane są zerowe, gdy zwracane dane są prawidłowe itp.

W przypadku kodu, który posiadasz, Twoim obowiązkiem jako programisty jest upewnienie się, że kod, który tworzysz, działa zgodnie z oczekiwaniami we wszystkich scenariuszach.

Venu b
źródło
1

Do tego testu użyłbym czegoś takiego jak EasyMock. Frameworki są idealnym sposobem na usunięcie zewnętrznych zależności od klasy i dają całkowitą kontrolę nad wynikiem zależności zewnętrznych podczas testów. Aby nieco rozszerzyć swój przykład:

class WebClass {

private WebServiceInterface webserviceInterface;

    void method(){
        R result = webServiceInterface.performWebServiceCall();
        ... do something with result
    }

    public void setWebServiceInterface(WebServiceInterface webServiceInterface){
        this.webServiceInterface = webServiceInterface;
    }
}


interface WebServiceInterface {

   R performWebServiceCall();

}


class WebClassTest {

private WebServiceInterface mock;    
private R sampleResult = new R();

    @Before
    public void before(){
        mock = EasyMock.createMock(WebServiceInterface.class);
    }


    @Test
    public void test() {
        WebClass classUnderTest = new WebClass();
        EasyMock.expect(mock.performWebServiceCall()).andReturn(sampleResult);
        classUnderTest.setWebServiceInterface(mock);
        classUnderTest.method();
        EasyMock.verify(mock);
    }
}

Pierwszą rzeczą, którą musisz zrobić, to wyodrębnić logikę w swojej klasie, w której używasz Jersey, aby uzyskać WebResource i wywołać usługę internetową do osobnej klasy. Utworzenie interfejsu dla tej klasy pozwoli ci stworzyć próbkę, do której możesz następnie dyktować zachowanie.

Po utworzeniu tego interfejsu można utworzyć próbkę za pomocą EasyMock, która zwróci określony obiekt zgodny z przypadkiem testowym. Powyższy przykład stanowi uproszczenie struktury podstawowego próbnego testu i działania interfejsu.

Aby uzyskać więcej informacji na temat ramek próbnych, zobacz to pytanie . Ponadto w tym przykładzie założono użycie Java, ale frameworki są dostępne we wszystkich językach i chociaż są zaimplementowane inaczej, będą działać w ten sam sposób

Richard
źródło
1

W tym przypadku możliwe są symulacje, ale nie są one potrzebne. Zamiast testowania jednostkowego method(), zamiast testowania jednostkowego tylko część, która obsługuje odpowiedź.

Wyodrębnij funkcję, która bierze ResponseData(dowolnego rodzaju), a następnie wykonuje akcję.

Zamiast kpić, teraz konstruujesz obiekt ResponseData i przekazujesz go.

Możesz pozostawić wezwanie usługi do pełnego testu integracji - który obejmie method()w całości

Daenyth
źródło
0

Co zrobiłem i działa:

  1. Wszystkie usługi sieciowe wywołujące połączenia kodowe mają mieć proxy.
  2. Serwer proxy wywołuje klasę, która statycznie wie, czy korzystamy z serwera proxy, i odpowiednio go przekierowuje. Egzaminy to po prostu HashMapy, które dla każdego żądania zwracają daną odpowiedź.
  3. Uruchom testy kilka razy w tej kolejności:

3.1 Najpierw testowane są wszystkie usługi sieciowe. Z każdej maszyny, nawet maszyny programisty. To są prawdziwe usługi internetowe, ale działają w środowisku programistycznym. Oznacza to, że usługi internetowe nigdy nie mogą być wyłączone lub odpowiadać na błędne wartości, ponieważ w przeciwnym razie każdy programista narzeka, że ​​nie może się skompilować.

3.2 Następnie uruchamiane są wszystkie testy jednostkowe wewnętrzne aplikacji. Oznacza to, że wszystkie usługi internetowe są wyśmiewane i testowane, uruchamiając te same testy co 3.1 (anbde powinny one również przejść, w przeciwnym razie wyśmiewane są błędne) i wywoływane przez prawdziwą aplikację tak, jakby były rzeczywiście używane. Jeśli próby są nieprawidłowe, możesz uruchomić test w wersji 3.1 i zapisać te wartości (żądanie, odpowiedź) w HashMap.

3.3 Następnie uruchamiane są te same testy co 3.2, ale tym razem z prawdziwymi usługami internetowymi działającymi w środowisku programistycznym.

Po wykonaniu wszystkich tych czynności w prawdziwym środowisku produkcyjnym wystarczy podać prawdziwy adres dla każdej usługi sieciowej. Mamy nadzieję, że nie wymaga to zbyt dużej zmiany w konfiguracji.

Guillermo Schwarz
źródło