I'm testing a certain class. This class is internally instantiating a "GetMethod" object that gets passed to a "HttpClient" object that gets injected into the tested class.
I'm mocking the "HttpClient" class, but I would need to modify the behaviour of one method of the "GetMethod" class too. I'm playing with ArgumentCaptor but I don't seem to be able to get a hold of the instantiated object in the "when" call.
Example:
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()
Mockito ArgumentCaptor is used to capture arguments for mocked methods. ArgumentCaptor is used with Mockito verify() methods to get the arguments passed when any method is called. This way, we can provide additional JUnit assertions for our tests.
ArgumentCaptor allows us to capture an argument passed to a method to inspect it. This is especially useful when we can't access the argument outside of the method we'd like to test.
We use argument captor with the methods like verify() or then() to get the values passed when a specific method is invoked. It is used to capture the method arguments. It is used to build a new ArgumentCaptor. It is used to return all the captured values.
The @Captor annotation is used to create an ArgumentCaptor instance which is used to capture method argument values for further assertions.
You cant use when
on getMethod, because getMethod is not a mock. It is still real object created by your class.
ArgumentCaptor has quite different purpose. Check section 15 here.
You could make your code more testable. Generally, classes that are creating new instances of other classes are difficult to test. Put some factory to this class for creating get/post methods, then in test mock this factory, and make it mock get/post methods.
public class YourClass {
MethodFactory mf;
public YourClass(MethodFactory mf) {
this.mf = mf;
}
public void handleHttpClient(HttpClient httpClient) {
httpClient.executeMethod(mf.createMethod());
//your code here
}
}
Then in test you can do:
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
You can also try some nasty hack, and Answer
and accessing GetMethod's private parts ;) by reflection. (This is really nasty 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;
}
});
Ok, this is how I've solved it. A little bit convoluted but couldn't find any other way.
In the test class:
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With