2010-09-01 5 views
6

Testuję pewną klasę. Ta klasa wewnętrznie tworzy instancję obiektu "GetMethod", która zostaje przekazana do obiektu "HttpClient", który zostaje wtryśnięty do testowanej klasy.Przechwyć argument w Mockito

Kpię z klasy "HttpClient", ale potrzebowałbym zmodyfikować zachowanie jednej metody z klasy "GetMethod". Gram z ArgumentCaptorem, ale wydaje mi się, że nie udało mi się uchwycić instancji obiektu w wywołaniu "kiedy".

Przykład:

HttpClient mockHttpClient = mock(HttpClient.class); 
ArgumentCaptor<GetMethod> getMethod = ArgumentCaptor.forClass(GetMethod.class); 
when(mockHttpClient.executeMethod(getMethod.capture())).thenReturn(HttpStatus.SC_OK); 
when(getMethod.getValue().getResponseBodyAsStream()).thenReturn(new FileInputStream(source)); 

Response:

org.mockito.exceptions.base.MockitoException: 
No argument value was captured! 
You might have forgotten to use argument.capture() in verify()... 
...or you used capture() in stubbing but stubbed method was not called. 
Be aware that it is recommended to use capture() only with verify() 

Odpowiedz

4

OK, tak to rozwiązałem. Trochę zawiłe, ale nie mogłem znaleźć innego sposobu.

W klasie testu:

private GetMethod getMethod; 

public void testMethod() { 
    when(mockHttpClient.executeMethod(any(GetMethod.class))).thenAnswer(new ExecuteMethodAnswer()); 
    //Execute your tested method here. 
    //Acces the getMethod here, assert stuff against it. 
} 

private void setResponseStream(HttpMethodBase httpMethod, InputStream inputStream) throws NoSuchFieldException, IllegalAccessException { 
    Field privateResponseStream = HttpMethodBase.class.getDeclaredField("responseStream"); 
    privateResponseStream.setAccessible(true); 
    privateResponseStream.set(httpMethod, inputStream); 
} 

private class ExecuteMethodAnswer implements Answer { 
    public Object answer(InvocationOnMock invocation) throws FileNotFoundException, 
                  NoSuchFieldException, IllegalAccessException { 
     getMethod = (GetMethod) invocation.getArguments()[0]; 
     setResponseStream(getMethod, new FileInputStream(source)); 
     return HttpStatus.SC_OK; 
    } 
} 
+0

Pisałeś o tym, kiedy edytowałem swoją odpowiedź. Cóż, obaj rozwiązaliśmy to w ten sam sposób :) – amorfis

+0

Tak, nie mogę znaleźć innego sposobu zrobienia tego za pomocą dostępnych narzędzi. Nasty hack :) ale kołysze, gdy działa! –

12

mogę użyć when na getMethod, ponieważ getMethod nie jest fałszywa. To wciąż prawdziwy obiekt stworzony przez twoją klasę.

ArgumentCaptor ma zupełnie inny cel. Sprawdź section 15 here.

Możesz sprawić, że Twój kod będzie bardziej testowalny. Ogólnie rzecz biorąc, klasy, które tworzą nowe instancje innych klas, są trudne do przetestowania. Umieść fabrykę w tej klasie, aby utworzyć metody get/post, a następnie przetestuj ją w tej fabryce i wykonaj próbne metody get/post.

public class YourClass { 
    MethodFactory mf; 

    public YourClass(MethodFactory mf) { 
    this.mf = mf; 
    } 

    public void handleHttpClient(HttpClient httpClient) { 
    httpClient.executeMethod(mf.createMethod()); 
    //your code here 
    } 
} 

Następnie w teście można zrobić:

HttpClient mockHttpClient = mock(HttpClient.class); 
when(mockHttpClient.executeMethod(any(GetMethod.class)).thenReturn(HttpStatus.SC_OK); 

MethodFactory factory = mock(MethodFactory.class); 
GetMethod get = mock(GetMethod.class); 
when(factory.createMethod()).thenReturn(get); 
when(get.getResponseBodyAsStream()).thenReturn(new FileInputStream(source)); 

UPDATE

Możesz także spróbować rzęsiste siekać i Answer i dostęp do części prywatnej getMethod za;) przez odbicie. (To jest naprawdę paskudny hack)

when(mockHttpClient.executeMethod(any(GetMethod.class))).thenAnswer(new Answer() { 
    Object answer(InvocationOnMock invocation) { 
    GetMethod getMethod = (GetMethod) invocation.getArguments()[0]; 

    Field respStream = HttpMethodBase.class.getDeclaredField("responseStream"); 
    respStream.setAccessible(true); 
    respStream.set(getMethod, new FileInputStream(source)); 

    return HttpStatus.SC_OK; 
    } 
}); 
+0

wiem instancji czyni zajęcia trudne do testowania, ale w tym przypadku fabryka byłaby przesada, a ja nie jestem wolny, aby zmienić implementację zbyt dużo. –