When using mocks, we often want to check the inputs to a function that was called on the mock. It’s probably a subject for another post whether you should rely on doing this, or whether you should make more code that just returns something, rather than calls something, but let’s agree that you will, at some point, want to check how a mock’s function was called.
With Mockito as our mocking framework of choice, here’s the hard way:
// given some test execution has happened // construct a fully-blown replica of what you think will have // been passed into your function under test SomeObject expectedInput = new SomeObject( ... ); verify(myMock).myMethod(eq(expectedInput));
Why is that hard?
Well, you need to predict a perfect replica of the input, which requires you to bind your test to the exact implementation. In some cases, certain fields, which are less interesting to the behaviour your testing, have to be specified so that the equals comparison works. Worse still, some fields that get arbitrary values at runtime have to have specific values pushed into them to make the test replicatable – timestamps, for instance.
In short, the above doesn’t always work well, so we often resource to the use of argument captors.
// given some test execution has happened // Find out how the method got called ArgumentCaptor<SomeObject> captor = ArgumentCaptor.for(SomeObject.class); verify(myMock).myMethod(captor.capture()); // read the thing you're interested in from the captor assertThat(captor.getValue().getInterestingProperty()) .isEqualTo(expected);
Put mildly, this sucks! Admittedly it partly sucks because we’re not using the @Captor annotation to declare the captor outside of the test body… but even then.
You have three activities to do with the captor, all of which are to enable you to check one fact. You have to construct one, use one to capture the value, and then read the value from the captor after the Mockito verify method is called. This must surely be more work than it’s worth.
The test also reads oddly. As pseudo-code:
- When the test was executed
- Given this captor
- Check the method was called and capture
- And check the captured value was expected
Since Mockito 2.1, used with Java 8, there has been a neater technique. You can use the argThat comparator with a verify call to inline your check in the verify method:
// given some test execution has happened // Find out how the method got called verify(myMock) .myMethod(argThat(someObject -> someObject.getInterestingProperty() .equals(expected)));
This, I think, makes for a more straightforward test. Verify that the method was called with an argument that matches a certain filter.
The reason this is possible is that Mockito moved away from using Hamcrest internally, replacing it with its own strongly-typed ArgumentMatcher interface, which is essentially a functional interface. This means you can replace it with a lambda.
An extra bonus of this technique is that you can also use argThat within the when or given constructs in Mockito. This means you can neatly specify how your mock will behave based on nuances of the input:
when(myMock.myMethod( argThat(someObject -> someObject.getInterestingProperty() .equals(expected)))) .thenReturn(42);
For more information on this please see the Mockito JavaDoc.
As a final thought, the folks over at Java Code Geeks would like to recommend their article on Java 8 in reference to this. Nice of them to be promoting a Java 8 article only five and a half years after it was written.