Hot questions for Using Mockito in method chaining

Top 10 Java Open Source / Mockito / method chaining

Question:

I have a Tuple mock class, whose getString(0) and getString(1) methods are expected to be called n times. Instead of writing something like,

when(tuple.getString(0)).thenReturn(logEntries[0]).thenReturn(logEntries[1])...thenReturn(logEntries[n - 1])

manually, I tried the following:

OngoingStubbing stubbingGetStringZero = when(tuple.getString(0)).thenReturn(serviceRequestKey);
OngoingStubbing stubbingGetStringOne = when(tuple.getString(1)).thenReturn(logEntries[0]);
for (int i = 1; i < n; i++) {
    stubbingGetStringZero = stubbingGetStringZero.thenReturn(serviceRequestKey);
    stubbingGetStringOne = stubbingGetStringOne.thenReturn(logEntries[i]);
}

The expected result is that all calls to tuple.getString(0) should return the String serviceRequestKey and each call to tuple.getString(1) should return a different String logEntries[i] ie. ith invocation of tuple.getString(1) returns ith element of logEntries array.

However, due to some odd reason, things are getting mixed up, and second invocation to tuple.getString(1) returns the String serviceRequestKey instead of logEntries[1]. What am I missing here?


Answer:

Well, the right way to do this would be:

import org.mockito.AdditionalAnswers;

String[] logEntry = // Some initialization code
List<String> logEntryList = Arrays.asList(logEntry);
when(tuple.getString(1)).thenAnswer(AdditionalAnswers.returnsElementsOf(logEntryList));

On each invocation, successive elements of logEntry array are returned. Thus, ith invocation of tuple.getString(1) returns ith element of logEntry array.

P.S: The example in documentation of returnsElementsOf (as of this writing) is not updated (it still uses ReturnsElementsOf example): http://docs.mockito.googlecode.com/hg/1.9.5/org/mockito/AdditionalAnswers.html#returnsElementsOf(java.util.Collection)it

Question:

I have a lot of code like the sample below that I need to test with Mockito.

return entityManager
    .createNamedQuery("queryName", Type.class)
    .setParameter("foo", "fish")
    .setParameter("bar", 42)
    .getSingleResult();

I have already injected a mocked EntityManager and have it return a mocked TypedQuery, etc. The problem is that I need to specify a rule like the following for every method in TypedQuery in order to have the test run successfully without NullPointerExceptions.

when(mockedTypedQuery.setParameter(any(String.class), any(Object.class)))
        .thenReturn(mockedTypedQuery);

Is there a more efficient way?


Answer:

What I did in that case, was totally inspired by this post: http://geeks.autotrader.co.uk/2014/03/mocking-fluent-interfaces-with-mockito.html

I had the following service:

public class TestService {

    public TypedQuery typedQuery;

    public List test(){
        final TypedQuery typedQuery1 = typedQuery.setParameter("a", "a");
        return typedQuery1.setParameter("b", "b").getResultList();
    }
}

And my test case was:

@RunWith(MockitoJUnitRunner.class)
public class ChainMethodsMockTest {

    TypedQuery mockedTypedQuery = fluentMock(TypedQuery.class);

    @InjectMocks
    TestService testService;

    @Test
    public void testMockito(){
        final ArrayList value = new ArrayList();
        value.add("a");
        value.add("b");
        Mockito.when(mockedTypedQuery.getResultList()).thenReturn(value);
        final List test = testService.test();
        Assert.assertEquals(2, test.size());
    }

    public static <T> T fluentMock(final Class<T> type) {
        return Mockito.mock(type, Mockito.withSettings().defaultAnswer(
                new ReturnsEmptyValues() {
                    @Override
                    public Object answer(InvocationOnMock invocation) {
                        Object defaultReturnValue = super.answer(invocation);
                        if (type.equals(invocation.getMethod().getReturnType())) {
                            return invocation.getMock();
                        } else {
                            return defaultReturnValue;
                        }
                    }
                }));
    }
}

fluentMock method that you can share and reuse among your project(s).

Question:

I have a following @Component with an injected object that makes a chain method call to get something, e.g.

@Component
public class MyInterceptor implements ClientHttpRequestInterceptor {

  @Autowired
  public MyProducer producer;

  @Override
  public ClientHttpResponse intercept(…) throws IOException {
    String val = producer.getProducer().getSomeVal(/*parameters*/); // LINE (1)
  }

}

And my test class is:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { MyInterceptor.class, MyProducer.class } )

public class MyInterceptorTest {

  private RestTemplate restTemplate = new RestTemplate();
  private MockRestSErviceServer mockServer;

  @Rule
  public MockitoRule rule = MockitoJUnit.rule();

  @Mock
  public MyProducer producer;

  @InjectMocks
  private MyInterceptor interceptor;

  @Before
  public void init() {
    //MockitoAnnotation.initMocks(this);
    producer = Mockito.mock(MyProducer.class, Mockito.RETURNS_DEEP_STUBS);
    // adding interceptor to RestTemplate
    mockServer = MockRestServiceServer.createServer(restTemplate);

    when(producer.getProducer().getSomeVal(null, null)).thenReturn("SomeValue");

  }

  @Test
  public void myTestMethod() {
    mockServer.expect(requestTo(/*some dummy URL*/)
      .andExpect(method(HttpMethod.GET))
      .andExcept(/*some header stuff omitted from MyInterceptor */)
      .andRespond(withSuccess(/*…*/));

    // This is going to trigger the Interceptor being invoked
    restTemplate.getForEntity("some dummy URL", String.class); // LINE (2)

    mockServer.verify();
  }
}

When the test executes LINE (2), and it invokes the interceptor, in LINE (1) I get a null pointer exception.

I was under the assumption that by enabling deep stubbing on a mock, I would be able to make a chain call and get an expected value, e.g. producer.getProducer().getSomeVal(), but it doesn't seem to be the case.

Do you know how I can get this working as expected?

P.S. I have tried different variation of adding MockitoAnnotation.initMocks() and getting rid of @Rule, or just @Autowired MyInterceptor in the test class which in this causes MyProducer not to be mocked at all, but nothing seems to work.

Note, MyProducer cannot be modified as it is from another project.


Answer:

You've mocked the MyProducer class, but you haven't provided a when for producer.getProducer().

So, when the code calls producer.getProducer() it will return the default mock value, which is null.

You could try a couple of different approaches:

when(producer.getProducer()).thenReturn(producer);

I'm not sure if that will work - it might.

Otherwise, you may be able to write a local test class that implements/extends whatever getProducer() returns, which in turn returns the appropriate test value when the correct parameters are passed to getSomeVal().