Modularly stubbing consecutive method calls with Mockito
java, mocking, mockito
Solution
When the standard methods for Mockito don't provide a mechanism to simulate your behavior, you can resort to implementing your own `Answer`. This is more work but provides extra flexibility.
Depending on your precise requirements, you could for instance create an `Answer` that returns a new element from a list of numbers, regardless of the request type (`int` or `short`). The variable `readList` can be a member that you can access from all the functions you use to set up your results.
final List<Integer> readList = new ArrayList<>();
// ... Fill readList with answers
Answer answerFromList = new Answer() {
Object answer(InvocationOnMock invocation) {
// Remove and return first element
return readList.remove(0);
}
}
when(reader.readInt()).thenAnswer(answerFromList);
when(reader.readShort()).thenAnswer(answerFromList);
Note that this `Answer` is a lot like the provided `ReturnElementsOf`, so you could use that directly as well.
Problem
I am trying to use Mockito to mock a "Reader" type class. Think of a data stream reader, it has methods to read various data types and advance the internal pointer after each read. ``` public interface Reader { int readInt(); short readShort(); } ``` The class being tested reads in various data structures from the data stream. For example, ``` public class Somethings { public List<Something> somethings; public Somethings(Reader reader) { somethings = new List<Something>(); int count = reader.readInt(); for (int i=0; i<count; i++) { somethings.add(readSomething(reader)); } } private Something readSomething(Reader reader) { int a = reader.readInt(); short b = reader.readShort(); int c = reader.readInt(); return new Something(a, b, c); } } ``` And finally, I have my test: ``` public class SomethingsTest { @Test public void testSomethings() { Reader reader = Mockito.mock(Reader.class); readCount(reader, 2); readSomething(reader, 1, 2, 3); readSomething(reader, 4, 5, 6); Somethings somethings = new Somethings(reader); Assert.assertEqual(2, somethings.size()); Assert.assertEquals(new Something(1, 2, 3), somethings.somethings.get(0)); Assert.assertEquals(new Something(4, 5, 6), somethings.somethings.get(1)); } private void readCount(Reader reader, int count) { when(reader.readInt()).thenReturn(count); } private void readSomething(Reader reader, int a, short b, int c) { when(reader.readInt()).thenReturn(a); when(reader.readShort()).thenReturn(b); when(reader.readInt()).thenReturn(c); } } ``` Unfortunately, this does not work. reader.readInt() always returns 6 for every invocation. And I understand why it returns 6. That is not my question. There are two options I can think of to fix the tests, but I don't particularly like either one. The first option would be something like: ``` public class SomethingsTest { @Test public void testSomethings() { Reader reader = Mockito.mock(Reader.class); when(reader.readInt()) .thenReturn(2) .thenReturn(1) .thenReturn(3) .thenReturn(4) .thenReturn(6); when(reader.readShort()) .thenReturn(2) .thenReturn(5); Somethings somethings = new Somethings(reader); Assert.assertEqual(2, somethings.size()); Assert.assertEquals(new Something(1, 2, 3), somethings.somethings.get(0)); Assert.assertEquals(new Something(4, 5, 6), somethings.somethings.get(1)); } } ``` This should work, but it's very monolithic and messy. It's difficult to see which return is for which piece of which structure, because they're all intermixed, with no structure. The second option I can think of is something like: ``` public class SomethingsTest { @Test public void testSomethings() { Reader reader = Mockito.mock(Reader.class); NewOngoingStubbing readIntStub = when(reader.readInt()); NewOngoingStubbing readShortStub = when(reader.readShort()); readCount(readIntStub, 2); readSomething(readIntStub, readShortStub, 1, 2, 3); readSomething(readIntStub, readShortStub, 4, 5, 6); Somethings somethings = new Somethings(reader); Assert.assertEqual(2, somethings.size()); Assert.assertEquals(new Something(1, 2, 3), somethings.somethings.get(0)); Assert.assertEquals(new Something(4, 5, 6), somethings.somethings.get(1)); } private void readCount(NewOngoingStubbing readIntStub, int count) { readIntStub.thenReturn(count); } private void readSomething(NewOngoingStubbing readIntStub, NewOngoingStubbing readShortStub, int a, short b, int c) { readIntStub.thenReturn(a); readShortStub.thenReturn(b); readIntStub.thenReturn(c); } } ``` This at least maintains the structure of the original, but having to pass a separate object for each method call you want to make on the stubbed object is... ugh. What would be the cleanest way to perform this test? Is there some option I'm missing here? Some functionality that I can leverage? I just started using Mockito tonight.. so I could very well be missing something.