Capture an argument in Mockito - java

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

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

Related

Mocking void unit tests with Mockito

Having had many issues in unit testing my application please help address my simplest issue - mocking void methods, per a few stack overflow articles and common references in articles. My Java code in a JBPM application WorkItemHandler follows the pattern of
public class EmailNotificationWorkItemHandler() extends AbstractLogOrThrowWorkItemHandler {
private static Utils utils = new Utils() ;
public void executeWorkItem(WorkItem workItem, WorkItemManager manager) {
if (<condition>) {
utils.insertAmsErrors(<param>, <param>, <param>, <param>, <param>);
return;
}
....
try {
RequiredParameterValidator.validate(this.getClass(), workItem);
...
}
I have been trying to stub Utils and its insertAmsErrors method to test RequiredParameterValidator.validate used later in executeWorkItem. The references imply that the void call to utils and its insertAmsErrors method could be a very basic stub of
Utils mockUtils = mock(Utils.class);
doAnswer(new Answer<Void>() {
public Void answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
//Mock mock = (Mock) invocation.getMock(); Doesn't solve NPE
return null;
}
}).when(mockUtils).insertAmsErrors(any(), any(), any(), any(), any()); // NPE here
but the stub throws a null pointer exception at "NPE here". Utils.insertAmsErrors signature is
public void insertAmsErrors(String id, ErrorCode error, String errorMsg,
StackTraceElement [] stackTrace, long processId)
I also considered using a spy, per another answer in the same posting, but my unit test is not calling insertAmsErrors on an instance of Utils, but rather executeWorkItem in EmailNotificationWorkItemHandler is making such a call.
How should I correctly mock the Utils class and its insertAmsErrors method so that I can test RequiredParameterValidator.validate in executeWorkItem?
The problem is not related to the fact that you're mocking a void method. The problem lies in your usage of the any() method.
when(mockUtils).insertAmsErrors(any(), any(), any(), any(), any());
The 5th required parameter is of type long and for this reason you should use the dedicated anyLong() method for it instead. This applies to all primitive types: anyInt() for int parameters, anyBoolean() for boolean parameters and so on...
The reason behind this is that Mockito's any() method returns null by design, inducing a NPE when there is an attempt to unbox it. See this related answer for more details about it.
(On a side note) Another potential problem could rise by the fact that your utils field is static, private and with an hardcoded dependency. This is not a good target for stubbing and you should probably rethink this by making, for example, the field non-static and then injecting the dependency.
public class EmailNotificationWorkItemHandler() extends AbstractLogOrThrowWorkItemHandler {
private Utils utils;
public EmailNotificationWorkItemHandler(Utils utils){
this.utils = utils;
}
...
}
This way you can pass the mocked object during your unit tests.

Calling method on real object instead calling on Mock object

I faced next issue when I have been writing Junit tests with using Mockito. My test invokes methods on real object instead mock object and I receive NullPointerException. Here is my code:
public class JSFUtilsTest {
public JSFUtilsTest() { }
JSFUtils jsfUtils = mock(JSFUtils.class);
FacesContext facesContext = ContextMocker.mockFacesContext();
ExternalContext extContext = mock(ExternalContext.class);
Application app = mock(Application.class);
ExpressionFactory exFactory = mock(ExpressionFactory.class);
ELContext elContext = mock(ELContext.class);
ValueExpression valExp = mock(ValueExpression.class);
#Test
public void testResolveExpression() {
when(jsfUtils.resolveExpression("expression")).thenAnswer(new Answer<Object>(){
public Object answer(InvocationOnMock invocation){
when(facesContext.getApplication()).thenReturn(app);
when(app.getExpressionFactory()).thenReturn(exFactory);
when(facesContext.getELContext()).thenReturn(elContext);
when(exFactory.createValueExpression(elContext, "expression", Object.class)).thenReturn(valExp);
when(valExp.getValue(elContext)).thenReturn(anyObject());
return valExp.getValue(elContext);
}
});
jsfUtils.resolveExpression(anyString());
verify(jsfUtils).resolveExpression(anyString());
assertNotNull(jsfUtils.resolveExpression(anyString()));
}
}
Instead calling resolveExpression() on Mock, I have got calling on JSFUtils object. JSFUtils.java and JSFUtilsTest.java are located in different packages. Can anybody help me? Thanks in advance!
I assume that this is just an example and in real test you do not call methods on the mock directly, but inject the dependencies to OUT.
You are expecting your mock to answer when you are calling jsfUtils.resolveExpression("expression"). In fact you are not calling it. I would suggest to change it to jsfUtils.resolveExpression(anyString()) and if you need it to be called with some specific string, you can check it the verify block: verify(jsfUtils).resolveExpression("expression");
Also calling jsfUtils.resolveExpression(anyString()); is not the right approach. Method anyString() is designed for stubbing not for real call.
Instead calling resolveExpression() on Mock, I have got calling on JSFUtils object.
Then do not create a mock, but a spy:
//JSFUtils jsfUtils = mock(JSFUtils.class);
JSFUtils jsfUtils = spy(new JSFUtils(/* mocks of dependencies if needed */));
But this is only needed if want to mock the return value of some other method in your class under test to force isolation of your unit.
This is not the case in your example as far as I see. So just write:
//JSFUtils jsfUtils = mock(JSFUtils.class);
JSFUtils jsfUtils = new JSFUtils(/* mocks of dependencies if needed */);
#Test
public void testResolveExpression() {
when(jsfUtils.resolveExpression("expression")).thenAnswer(new Answer<Object>(){
public Object answer(InvocationOnMock invocation){
when(facesContext.getApplication()).thenReturn(app);
when(app.getExpressionFactory()).thenReturn(exFactory);
when(facesContext.getELContext()).thenReturn(elContext);
when(exFactory.createValueExpression(elContext, "expression", Object.class)).thenReturn(valExp);
when(valExp.getValue(elContext)).thenReturn(anyObject());
return valExp.getValue(elContext);
}
});
jsfUtils.resolveExpression(anyString());
verify(jsfUtils).resolveExpression(anyString());
assertNotNull(jsfUtils.resolveExpression(anyString()));
This does not make any sense:
You mock the method of your class under test (cut) and then call that very same method. This way you do not verify the behavior of your cut but the behavior of the mocking framework.
Also you call the method twice within the same test method. You should avoid that.
You should change your test to this:
#Test
public void testResolveExpression() {
// arrange (test case specific setup of mocks and DTOs)
when(facesContext.getApplication()).thenReturn(app);
when(app.getExpressionFactory()).thenReturn(exFactory);
when(facesContext.getELContext()).thenReturn(elContext);
when(exFactory.createValueExpression(elContext, "expression", Object.class)).thenReturn(valExp);
when(valExp.getValue(elContext)).thenReturn(anyObject());
// act (call real method on real object)
Object result = jsfUtils.resolveExpression("a valid input");
// assert
assertNotNull(result );
}

PowerMock mock external library

Really short question: How can I mock response.getContentType() ?
(Using PowerMock + TestNG)
I'm not calling any new() methods.
I'm trying to mock class, that is result of method execution of some other class.
The class under test:
class ClassToBeMocked {
public String getJsonPage(String jsonUrl) throws IOException {
WebClient webClient = new WebClient(BrowserVersion.CHROME);
final Page page = webClient.getPage(jsonUrl);
final WebResponse response = page.getWebResponse();
final String cType = response.getContentType();
if (cType.equals("application/json") || cType.equals("application/hal+json")) {
return response.getContentAsString();
}
throw new IllegalArgumentException("Unexpected response type " + response.getContentType());
}
}
Test itself
#PrepareForTest( { WebResponse.class, ClassToBeMocked.class})
#PowerMockIgnore("javax.net.ssl.*")
public class UrlPullerTest extends PowerMockTestCase {
#Test
public void testGetPage() throws Exception {
WebResponse mockwebResposne = PowerMockito.mock(WebResponse.class);
PowerMockito.when(mockwebResposne.getContentType()).thenReturn("wrongType");
ClassToBeMocked classToBeMocked = new ClassToBeMocked();
classToBeMocked.getJsonPage("http://google.com");
}
}
You wouldn't. Your problem is that you created hard to test code, by putting that new WebClient call into your source code. That leads to direct coupling of implementations.
You should use dependency injection instead (for example to inject a factory that creates WebClient objects for you). Doing so, you can do all your work with power-less frameworks such as EasyMock or Mokito.
Hint: far too often, the usage of PowerMock is an indication, that your design could be improved. No idea what I am talking about? Then watch these videos. Each one worth each minute!

Using mockito captor to capture the argument sent to a method

I would like to capture the argument inputMessage in the readInternal() of the CustomMessageConverter class when client submits a message. client object here is not a mock object.
#Component
public class CustomMessageConverter extends AbstractHttpMessageConverter<Object> {
...
#Override
protected Object readInternal(final Class<? extends Object> clazz, final HttpInputMessage inputMessage) throws IOException,
HttpMessageNotReadableException {
...do something here...
}
...
}
In AbstractHttpMessageConverter class:
#Override
public final T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException {
return readInternal(clazz, inputMessage);
}
So, I wrote a mockito class to capture it.
But, it is still going to the regular converter class. Could you suggest what I am doing wrong?
#Mock
CustomMessageConverter mockMessageConverter;
#Captor
private ArgumentCaptor<HttpInputMessage> inputMessage;
#Test
public void test() {
when(mockMessageConverter.read(CustomMessage.class, inputMessage.capture())).thenReturn(null);
client.submitMessage(Message);
verify(mockMessageConverter, times(1));
Object actual = customConverter.read(CustomMessage.class, inputMessage.getValue());
}
Per the limitations of Mockito, you can't stub or verify final methods. In short, because the method is marked final, Java skips looking up the method in a method table (or calling the Proxy object that Mockito creates) and compiles in a call to the method implementation directly. With that direct-compiled call there is no opportunity for Mockito to substitute its own answers for stubbing or call-tracking logic for verification.
Try mocking readInternal instead, or refactor your code to rely on an interface instead of an implementation. Mockito can mock any method on an interface, because there are no finality or visibility problems allowed within interfaces.

Is it possible to mock a single method in an already existing object?

For an integration test, I need to mock a specific method in a java service client without destroying the rest of the information in it. It has no self-constructor, so a solution like this is out of the question:
private DBClient mockClient = new DBClient(alreadyExistingClient){
#Override
void deleteItem(Item i){
//my stuff goes here
}
};
Is there a way to mock the deleteItem method such that the credentials, endpoints, etc... are preserved in an existing DBClient object?
edit: mockito is not available for use in this case
You can use a Dynamic Proxy to intercept any method invocation you want, so you can decide between invoking the real method or do whatever you want instead.
This is an example of how to intercept the method Set.add(), you can do exactly the same for deleteItem()
package example.dynamicproxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Set;
public class SetProxyFactory {
public static Set<?> getSetProxy(final Set<?> s) {
final ClassLoader classLoader = s.getClass().getClassLoader();
final Class<?>[] interfaces = new Class[] {Set.class};
final InvocationHandler invocationHandler = new InvocationHandler() {
#Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
if (method.getName().equals("add")) {
System.out.println("add() intercepted");
// do/return whatever you want
}
// or invoke the real method
return method.invoke(s, args);
}
};
final Object proxy = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
return (Set<?>) proxy;
}
}
You could go lo-fi and create a sub-class of the DBClient class. To this subclass, pass the instance of DBClient you want to mock.
Use composition inside the sub-class, and delegate all method calls to the original DBClient, all except the one you want to mock. Add your mock implementation to the method you want.
This is not as reusable as a mocking framework, but should work.
DBClient mockDbClient = new DBClient() {
private DBClient dbClientDelegate;
public void setDelegate(DBClient dbClient) {
dbClientDelegate = dbClient;
}
//override all methods.
//delegate to the corresponding method of the dbClientDelegate instance
//overide the method you want to mock, add asserts for method arguments
//return mock data as appropriate
}
mockDbClient.setDelegate(preinstantiatedDbClient);
//inject mockDbClient to test class
//call test class / method
Hope this helps.
In Mockito 2+ you can use spy feature for this purpose:
PrintStream realSystemOut = System.out;
realSystemOut.println("XXX");
PrintStream mockedSystemOut = Mockito.spy(realSystemOut);
Mockito.doNothing().when(mockedSystemOut).println(Mockito.anyString());
mockedSystemOut.println("YYY");
Output:
XXX

Categories

Resources