Capture an argument in Mockito

23,732

Solution 1

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

Solution 2

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;
  }
});
Share:
23,732

Related videos on Youtube

Iker Jimenez
Author by

Iker Jimenez

#SOreadytohelp

Updated on July 09, 2022

Comments

  • Iker Jimenez
    Iker Jimenez almost 2 years

    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()
    
  • Iker Jimenez
    Iker Jimenez almost 14 years
    I know instantiation makes classes difficult to test, but in this case a factory would be overkill, and I'm not free to change the implementation too much.
  • amorfis
    amorfis almost 14 years
    You posted it when I was editing my answer. Well, we both resolved it the same way :)
  • Iker Jimenez
    Iker Jimenez almost 14 years
    Yes, I cannot find any other way of doing it with the available tools. Nasty hack :) but it rocks when it works!