Over a million developers have joined DZone.

Mockito Custom Answers

Custom argument matchers and custom answer behavior allow us to use Mockito to mock complex and responsive behavior, making for highly realistic tests.

· DevOps Zone

The DevOps zone is brought to you in partnership with Sonatype Nexus. The Nexus suite helps scale your DevOps delivery with continuous component intelligence integrated into development tools, including Eclipse, IntelliJ, Jenkins, Bamboo, SonarQube and more. Schedule a demo today

In a previous article I introduced using Mockito to mock a database by mocking all the classes involved in the JDBC API. However, that simple example didn't include any complex cases, like wanting the mocks to respond differently to different kinds of inputs.

Argument Matchers

Mockito argument matchers are very powerful. We saw a simple example in the last article:


This creates a rule in the mock object that is only invoked when the getString() method is called with an argument of 2. Otherwise some other rule will fire, or Mockito will perform its default (which generally means returning null).

We can do more than just supply a single argument. We can also supply a matcher, which will be run against the argument. (When we specify a static argument we are implicitly using the eq() matcher, which uses regular Java equals().) Mockito has a number of matchers to choose from and custom matchers are easy to write:

// Emulate the real service error handling
when(svc.updateDistance(argThat(new ArgumentMatcher<Integer> {
  public boolean matches(Integer i) {
    return null != i || i < 0;
}).thenThrow(new IllegalArgumentException());


Our previous mocks were too simple in that they didn't ensure anything about the query that was created and the parameters that were passed in. We would like to ensure that our database access code passes in the expected ID when retrieving data, that it doesn't incorrectly hold onto any data, and that it correctly deals with the case where the database doesn't return any results.

It might help to show the test we would like to write. Note that there is nothing Mockito-specific about this test; we are just describing our expectations after interacting with the code.

    public void wrongIdReturnsNull() throws Exception {
        PersonDao dao = new PersonDao(ds);
        assertEquals(p, dao.retrieve(1));
        for (int i = 2; i < 10; i++) {
        assertEquals(p, dao.retrieve(1));

We start with an empty table, add an item, and verify that only the expected item is there. We also ensure that retrieving it the first time wasn't a fluke.

To make this happen with Mockito, we are going to need a more intelligent mock; it is going to have to know whether the "right" ID was passed in to the prepared statement before it decides what kind of result set to return.

An Aside: Verify

Instead of doing things this way, we could write a similar test using Mockito's verify() support. Mockito keeps a record of all method calls received by a mock object and allows us to verify that the right calls were made:

// Perform the action that causes the mock to be invoked
Person p = dao.retrieve(1);
// Check that it was invoked correctly
verify(stmt).setInt(1, 1);

This has the advantage of simplicity, and it's probably all we would really need to test code as simple as my example. But it has the disadvantage that you have to be careful how many things are interacting with your mock objects, how many times, and potentially in what order. The result is that it can contribute to the brittleness of the tests. So while it's a very good approach, and the right approach for simple cases, it's worth knowing how to do things another way as well.

Argument Matching and Memory

So we would like to try to write this unit test using argument matchers. However, the situation gets much more complicated than our custom argument matcher above, because we are using prepared statements. As a result, the parameters we are interested in are set in earlier methods, and the call to executeUpdate() that actually returns the result set doesn't have any parameters.

So what we need is a kind of "memory", where we store the argument that our mock receives in one call and then use it to make a decision in a later call. That ends up looking like this:

        doAnswer(new Answer<Void>() {
            public Void answer(InvocationOnMock invocation) throws Throwable {
                lastRetrieve = (int)invocation.getArguments()[1];
                return null;
        }).when(retrieveStmt).setInt(eq(1), anyInt());

        when(retrieveStmt.executeQuery()).thenAnswer(new Answer<ResultSet>() {
            public ResultSet answer(InvocationOnMock invocation) throws Throwable {
                return lastRetrieve == 1 ? rs : nullRs;

It's worth taking some time to unpack this. First, the doAnswer() bit. Mockito's fluent API has some trickery associated with it, because from the Java compiler's perspective, the mock object isn't special; it's just one more object of whatever type we're mocking. Mockito can work around that when the method we're configuring returns some value. But when it's a void method, there's no way to attach the OngoingStubbing to anything. So Mockito provides a slightly different syntax where we provide the outcome we want first, then we specify the method and argument matcher last.

Specifying behavior for a void return isn't generally useful for a mock object, since mostly we configure mocks to just return whatever is expected by the code under test. But there is value in being able to look at incoming parameters and throw exceptions, or in this case to save off the values that were passed in. 

Once we've saved the ID that is being passed into the mock prepared statement, we can use it to decide which result set to use. In this case, if the ID equals 1, we return a mock result that's configured to return content, and if it's anything else, we return a mock result that's configured with no content.

One other note on the configuration above. We explicitly use eq() in the setInt() matcher. Because of the way it's written internally, Mockito either requires all of the parameters to be specified explicitly as argument matchers, or none to be. So we could use setInt(1, 1) or setInt(eq(1), anyInt()), but not setInt(1, anyInt()).

Wrapping Up

Now that we have what we want for this unit test, we could expand this mocking behavior pretty easily to provide different content for different IDs that are passed in. Or we could configure it to only return the "record" from the "table" in cases where it's been previously created. But at some point we would move from checking out the code under test to checking out the behavior of the mocks, which isn't really the purpose of this exercise.

That gets to an important rule about using mocking when testing, which is that our focus needs to change a little. When integration testing, we want to make sure that the collection of classes does the "right thing" at a macro level (ultimately the right thing from the user's perspective). But when focusing on a single class or method in unit testing, typically the "right thing" just involves: handling error cases correctly; calling other methods as expected; and returning expected values. The fact that our mock object would let us write a test that pretended data was in the database before we inserted it doesn't bother us in the slightest.

The above example barely scratches the surface of the advanced things we can do with Mockito. The only downside is that the more of this kind of thing we do, the less we are producing the kind of fluent configuration that makes Mockito easy to work with and maintain. But for cases where we have to test interaction between our code under test and a complex API, it's nice to have this kind of access to the underlying behavior.

The DevOps zone is brought to you in partnership with Sonatype Nexus. Use the Nexus Suite to automate your software supply chain and ensure you're using the highest quality open source components at every step of the development lifecycle. Get Nexus today

mockito,mock objects

The best of DZone straight to your inbox.

Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}