2011-11-25 8 views
17

Mam następującą sytuację:karczowarki metody, które manipuluje parametry z Mockito

class Worker { 
    public Integer somework() { 
     Integer k=0; 
     Helper h= new Helper(); 
     h.change(k); 
     return k; 
    } 
} 

class Helper { 
    public void change(Integer k) { 
    //k = Some calcs 
    } 
} 

Robię unitests dla Worker i oczywiście chcę drwić Helper klasę tak, że jego metoda change zawsze umieścić 1 do k.

Moja realna sytuacja jest bardziej skomplikowana, ale ten kod reprezentuje problem. Dzięki za pomoc.

Odpowiedz

2

Zmieniłbym podpis metody i sprawił, że wystąpił jako instancja Helper. Osoba wywołująca utworzy pomocnika i przekaże go do metody somework. Test przejdzie pozornego pomocnika.

Jeśli nie jest to możliwe, przynajmniej zadzwonić chronionej metody fabryki do tworzenia pomocnika, i drwić z tej metody fabryki podczas testowania metody somework w celu uczynienia go zwrócić mock pomocnika:

class Worker { 
    public Integer somework(){ 
     Integer k=0; 
     Helper h= createHelper(); 
     h.change(k); 
     return k; 
    } 

    // this method may be mocked when testing somework, to return a mock helper. 
    protected Helper createHelper() { 
     return new Helper(); 
    } 
} 
+1

Um .. Ok, to opcja. Ale co, jeśli nie jestem w stanie zmienić obecnego kodu, czy jest to "właściwe", aby zmienić rzeczywisty projekt, aby ułatwić tworzenie testów? – TryHarder

+1

Tak, zgadza się. Pisanie testowalnego kodu jest ważną częścią dobrej pracy programisty. Właśnie dlatego frameworki IOC są tak popularne: promują projekt, który umożliwia testowanie kodu. –

+0

Dziękuję. Opuszczę wątek, może jest jeszcze opcja, aby zrobić to, co zamierzałem z mockito, tylko dla celów edukacyjnych. Tymczasem będę postępować zgodnie z twoją radą. – TryHarder

4

Dla punktu @JB Nizet, tak, dobrze jest zrestrukturyzować testowalność. Często refaktoryzacja, aby uczynić kod bardziej testowalnym, prowadzi do kodu, który jest lepszy z innych powodów - rozdzielenia obaw i tym podobnych. Nie zawsze jest to możliwe. Powiedzmy, że to nie jest twój kod lub masz inne wymaganie, aby zostawić to w spokoju (ponieważ wiele innych klas polega na tym, że tak jest) lub cokolwiek innego.

Jeśli dobrze rozumiem, co trzeba zrobić, myślę, że można to zrobić ze szpiegiem:

Worker workerUnderTest = new Worker(); 
Worker spiedWorkerUT = spy(workerUnderTest); 
Helper mockHelper = mock(Helper.class); 
when(spiedWorkerUT.createHelper()).thenReturn(mockHelper); 
Integer actual = spiedWorkerUT.someWork(); 
verify(mockHelper).change(0); 

Następnie użyj spiedWorkerUT zamiast workerUnderTest do uruchamiania testów.

Nie zawsze można uniknąć tworzenia czegoś, co chce się udawać. W tym celu istnieje PowerMock.

Helper mockHelper = mock(Helper.class); 
whenNew(Helper.class).withNoArguments().thenReturn(mockHelper); 
+0

Dzięki za wejście. To, co faktycznie próbuję zrobić, to zmuszanie 'mockHelper' do zmiany jego zachowania po wywołaniu metody' change' z 'Worker'. Łatwo jest osiągnąć dziedziczenie '' mockHelper' od 'Helper' i przesłonić' change' method, ale może mockito może mi pomóc bez nowej klasy? – TryHarder

+0

Jestem zdezorientowany. Co chcesz zmienić? Czy "zmiana" zmienia argument, który przekazujesz, lub zmienia stan Pomocnika w przypadku innych połączeń? – jhericks

+0

Zmienia argument ('k') – TryHarder

18

Mam metodę z definicji takiego:

class Template{ 
    public void process(Object rootMap, StringWriter out){ 
....... 
    } 
} 

pokażę, jak można zmienić/zmodyfikować "out" (StringWriter) Referencje.

private final Template mocktTemplate = mock(Template.class); 
doAnswer(new Answer<StringWriter>() { 

      public StringWriter answer(InvocationOnMock invocation) 
        throws Throwable { 
       Object[] args = invocation.getArguments(); 
       if (args[1] instanceof StringWriter) { 
        StringWriter stringWriter = (StringWriter) args[1]; 
        stringWriter.write("Email message"); 
       } 
       return null; 
      } 
     }).when(this.mocktTemplate).process(anyObject(),any(StringWriter.class)); 

Teraz, kiedy robią rzeczywiste połączenia jak:

msgBodyTemplate.process(model, msgBodyWriter); 

roku wartość StringBuffer ref w msgBodyWriter będzie "wiadomości e-mail"; niezależnie od wcześniejszej wartości.

+1

To jest niesamowite! – devdanke

4

Myślę, że DoAnswer to najlepsza metoda radząca sobie z metodą void, w której metoda manipuluje określonymi parametrami.

doAnswer(new Answer() { 
    public Object answer(InvocationOnMock invocation) { 
     Object[] args = invocation.getArguments(); 
     Mock mock = invocation.getMock(); 
     return null; 
    }}) 
.when(mock).someMethod(); 

Zasadniczo, po uzyskaniu argumentów można wykonać dowolną modyfikację. There's a blog post wyjaśnienie to trochę.

Powiązane problemy