2011-01-26 6 views
9

Używam EasyMock do wyśmiewania obiektów w moich testach. Ale jak wyśmiać obiekty, które są tworzone gdzieś indziej w moim kodzie? Spójrz na poniższy kod psudo. Chcę kpić z WebService # getPersonById, jak to zrobić?Jak wyśmiać obiekty, których nie mogę utworzyć w moich testach?

public class Person { 
    public Person find(int id) { 
    WebService ws = new WebService(); 
    return ws.getPersonById(id); 
    } 
} 

public class PersonTest { 
    testFind() { 
    // How do I mock WebService#getPersonById here? 
    } 
} 

Odpowiedz

0

Zamiast tego użyłem JMockit. Posiada wsparcie dla kpiny z wszystkich instancji klasy.

+2

Przykro mi to mówić, ale to wstyd. IMHO Jedną z wielkich zalet rozwoju opartego na testach jest to, że jesteś zmuszony do przemyślenia i przemyślenia swojego projektu. Znalezienie czegoś, co nie da się łatwo przetestować pod kątem interfejsów, jest dobrym znakiem zapachu kodu. @hvgotcodes opracował ładną ścieżkę, w jaki sposób można sprawić, by kod był łatwiejszy do utrzymania, elastyczny i testowalny. Nie chciałbym tego wyrzucić za zwykłe obejście. –

9

Szydełkowanie zazwyczaj działa dobrze, jeśli używasz inwersji wtrysku sterującego i zależności, aby połączyć swoje usługi. Więc człowiek powinien wyglądać

public class Person() { 
    WebService ws = null; 

    // or use setters instead of constructor injection 
    Persion(WebService ws) { 
    this.ws = ws; 
    } 
    public Person find(int id) { 
    return ws.getPersonById(id); 
    } 
} 

miejmy nadzieję, że jest oczywiste, że z tej zmiany, można teraz tworzyć makiety i makiety kontrolę WebService i po prostu podłączyć go w teście, ponieważ podczas tworzenia Osoba do testowania , możesz przekazać symulację do konstruktora (lub setera, jeśli pójdziesz tą trasą).

w normalnym środowisku, kontener IoC wstrzyknie prawdziwą usługę internetową w.

Teraz, jeśli nie chcemy mieć do czynienia z tym wszystkim IoC rzeczy, co trzeba zrobić, to oddzielenie od Twojego usługa twoja Osoba (która powinna być nazywana PersonService lub czymś, nie tylko Osobą, która oznacza podmiot). Innymi słowy, sposób w jaki napisany jest kod, można używać tylko jednego typu usługi WebService. Musisz to zrobić, aby osoba potrzebowała tylko pewnego rodzaju usługi WebService, a nie konkretnej, którą masz na stałe.

Wreszcie, w kodzie zapisanym, WebService jest klasą, a nie interfejsem. WebService powinien być interfejsem, a ty powinieneś wdrożyć jakąś implementację. EasyMock działa dobrze z interfejsami; może być w stanie kpić z konkretnych klas (od jakiegoś czasu już go użyłem), ale jako zasada projektowania należy określić wymagany interfejs, a nie konkretną klasę.

+0

+1 - to niemal dokładnie co dodałem w mojej (teraz usuniętej) odpowiedzi. –

+0

+1 Dobra odpowiedź. Sposób bycia nauczycielem. – jwir3

+1

Sądzę, że można powiedzieć, że istnieje zapach kodu, skoro nie można wtedy wyśmiać obiektu? :-) Sądzę, że muszę przemyśleć moją implementację. Wielkie dzięki za wspaniałą odpowiedź i przykład. – Sven

0

Najpierw trzeba wykonać próbkę ws, zwykle wstrzykując ją.

public abstract class Person() { 
    public Person find(int id) { 
    WebService ws = createWebService(); 
    return ws.getPersonById(id); 
    } 
    protected abstract WebService createWebService(); 
} 

Następnie można wsunąć go i używać EasyMock.expect skonfigurować powrocie

public class PersonTest() { 
    testFind() { 
    WebService mock = EasyMock.createMock(WebService.class); 
    Person p = new Persion() { 
     protected WebService createWebService() { 
     return mock; 
     } 
    } 
    EasyMock.expect(mock.getPersonById()).andReturn(dummyValue); 
    //Test code 
    } 
} 

Będziesz także potrzebował PersonImpl mieć prawdziwy stworzyć metodę.

1

Myślę, że brakuje ci znacznie większego problemu. Trudność w testowaniu próbuje ci coś powiedzieć, że posiadanie obiektu Person (części domeny), który również korzysta z usługi zdalnej w celu znalezienia kolejnych instancji (części systemu), miesza obawy. Oddziel otrzymanie od obiektu Person s od obiektu Person, a otrzymasz czystszy, bardziej przenośny kod.

Nie mylić natychmiastowej wygody (mam obiekt Person w ręce, więc użyję go do uzyskania więcej) z możliwością konserwacji.

3

Nie można tego zrobić za pomocą EasyMock (lub większości innych fałszywych interfejsów API). Z JMockit, z drugiej strony, taka próba byłaby bardzo prosty i elegancki:

public class PersonTest 
{ 
    @Test 
    public testFind(@Mocked final WebService ws) { 
     final int id = 123; 

     new NonStrictExpectations() {{ 
      ws.getPersonById(id); result = new Person(id); 
     }}; 

     Person personFound = new Person().find(id); 

     assertEquals(id, personFound.getId()); 
    } 
} 

Tak więc, gdy prowadzimy w sytuacji, gdy testy jednostkowe nie mogą być napisane w pierwszym, nie możemy automatycznie stwierdzić, że testowany kod jest niesprawdzalny i wymaga refakturowania.Czasami tak się stanie, ale na pewno nie zawsze. Być może problem tkwi nie w testowanym kodzie, ale w ograniczeniach konkretnego używanego narzędzia szyderstwa.

Powiązane problemy