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
Please enter at least three characters to search
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

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

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

  • Tired of Spring Overhead? Try Dropwizard for Your Next Java Microservice
  • The Smart Way to Talk to Your Database: Why Hybrid API + NL2SQL Wins
  • How To Build Resilient Microservices Using Circuit Breakers and Retries: A Developer’s Guide To Surviving
  • The Future of Java and AI: Coding in 2025
  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
Likes (2)
Comment
Save
Tweet
Share
6.5K 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
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and 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:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!