DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Low-Code Development: Leverage low and no code to streamline your workflow so that you can focus on higher priorities.

DZone Security Research: Tell us your top security strategies in 2024, influence our research, and enter for a chance to win $!

Launch your software development career: Dive head first into the SDLC and learn how to build high-quality software and teams.

Open Source Migration Practices and Patterns: Explore key traits of migrating open-source software and its impact on software development.

Related

  • Beginners Guide to Mocking in Scala
  • Unlocking the Power of Configuration Management Database (CMDB)
  • Mastering Database Unit Testing: A Full Guide and 5 Essential Tools
  • Common Mistakes to Avoid When Writing SQL Code

Trending

  • Spring AI: How To Write GenAI Applications With Java
  • Integration Testing With Keycloak, Spring Security, Spring Boot, and Spock Framework
  • Leveraging Microsoft Graph API for Unified Data Access and Insights
  • Performance and Scalability Analysis of Redis and Memcached
  1. DZone
  2. Testing, Deployment, and Maintenance
  3. Testing, Tools, and Frameworks
  4. Mockito Custom Answers

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.

By 
Alan Hohn user avatar
Alan Hohn
·
Sep. 13, 16 · Tutorial
Like (2)
Save
Tweet
Share
5.8K Views

Join the DZone community and get the full member experience.

Join For Free

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:

when(rs.getString(2)).thenReturn(p.getFirstName());

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());

Applications

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.

    @Test
    public void wrongIdReturnsNull() throws Exception {
        PersonDao dao = new PersonDao(ds);
        dao.create(p);
        assertEquals(p, dao.retrieve(1));
        for (int i = 2; i < 10; i++) {
            assertNull(dao.retrieve(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>() {
            @Override
            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>() {
            @Override
            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.

Mockito unit test Database Mock object IT

Opinions expressed by DZone contributors are their own.

Related

  • Beginners Guide to Mocking in Scala
  • Unlocking the Power of Configuration Management Database (CMDB)
  • Mastering Database Unit Testing: A Full Guide and 5 Essential Tools
  • Common Mistakes to Avoid When Writing SQL Code

Partner Resources


Comments

ABOUT US

  • About DZone
  • Send feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: