2010-09-01 33 views
6

我正在測試某個類。這個類在內部實例化一個「GetMethod」對象,該對象被傳遞給被注入到被測試類中的「HttpClient」對象。我正在嘲笑「HttpClient」類,但我需要修改「GetMethod」類的一個方法的行爲。我在玩ArgumentCaptor,但我似乎無法在「何時」調用中獲得實例化對象。在Mockito中捕獲一個參數

例子:

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)); 

響應:

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() 

回答

4

好的,這就是我解決它的方法。有點複雜,但找不到任何其他方式。

在測試類:

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

您在編輯我的答案時發佈了它。那麼,我們都以同樣的方式解決它:) – amorfis 2010-09-02 15:59:01

+0

是的,我找不到任何其他方式使用可用的工具。令人討厭的黑客:)但它的工作時,它岩石! – 2010-09-02 23:06:43

12

你不能使用上getMethod when,因爲getMethod不是模仿。它仍然是你的班級創建的真實對象。

ArgumentCaptor具有完全不同的目的。檢查section 15 here

你可以讓你的代碼更具可測性。通常,創建其他類的新實例的類很難測試。把一些工廠放到這個類中來創建get/post方法,然後在測試這個工廠的模擬器,並且使它變成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 
    } 
} 

然後測試,你可以這樣做:

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

您也可以嘗試一些討厭的黑客攻擊,並Answer和訪問GetMethod的私處;)通過反射。 (這真是令人討厭的黑客)

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

我知道實例化,使類很難測試,但在這種情況下,工廠將是矯枉過正,我不得隨意更改實施太多。 – 2010-09-02 09:39:07