Got a void method that you need to handle with your Mockito unit testing? No problem, I'll show you how to do that here.

Usually, it's best to test the return value from a method. That's how you make sure you get back what you expected.

But alas, some methods you need to test might return nothing at all. But you still want to make sure they're working.

Here's how you can make that happen.

The Use Case

For testing purposes, we'll go old-school with a simple DAO object that persists customer data for an ecommerce application.

And you'll write a unit test for a method that updates the customer's phone number. That same method will update the Customer object, but it won't return anything. 

Your unit test will check to make sure the code updated the phone number in the Customer object.

POM-POMs

We're going to work with a ridiculously simple application here, but it does include a couple of dependencies. Here's what the POM file looks like:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>us.careydevelopment.mockito</groupId>
	<artifactId>CrudApp</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	
	<dependencies>
		<dependency>
		    <groupId>org.mockito</groupId>
		    <artifactId>mockito-junit-jupiter</artifactId>
		    <version>4.2.0</version>
		    <scope>test</scope>
		</dependency>
		<dependency>
		    <groupId>org.junit.jupiter</groupId>
		    <artifactId>junit-jupiter-api</artifactId>
		    <version>5.8.0</version>
		    <scope>test</scope>
		</dependency>
	</dependencies>
</project>

You'll just need to hone in on the dependencies if you're interested in working with Mockito and the latest flavor of JUnit.

Note that you can also (probably) get away with using different versions. You don't necessarily need the latest and greatest.

The Customer Is Always Right

Here's what the Customer class looks like:

public class Customer {

	public Integer id;
	public String firstName;
	public String lastName;
	public String phoneNumber;
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getPhoneNumber() {
		return phoneNumber;
	}
	public void setPhoneNumber(String phoneNumber) {
		this.phoneNumber = phoneNumber;
	}
}

Nothing very complicated going on there.

Keep in mind: in the test case you're about to write, you'll make sure that the phone number got updated. 

DAOing for Your Love

Next, take a look at the data access object (DAO) class. It's pretty simple:

public class CustomerDao {
	
	public void updatePhoneNumber(String phoneNumber, Customer customer) {
		//database work goes here
		//this code will update the given customer object with the phone number
	}
}

Yep. You can't get much more simple than a single method that does absolutely nothing.

But that's all you need for the purposes of this guide. If this were a real application, you'd put the code that persists the new phone number in the place where you see the comments.

Note that the method accepts two parameters:

  • the updated phone number
  • the Customer object

That Customer object gets updated with the new phone number. But it doesn't get returned.

Instead, a big, fat void gets returned. That is to say, nothing gets returned.

 Still, the Customer object gets updated. So the unit test can check for an update.

If This Were a Real Application, Cont'd...

You might be asking yourself: "Why not just write the method that updates the Customer object and then check it like you would check an object with any other unit test?"

Here's why: in the real world, this method would use JDBC or JPA to handle persistence. That persistence effort would also update the Customer object.

But you don't want to do that persistence call with a unit test. You want to mock it.

So you're in a pickle. If you call the actual object instead of a mock, you'll participate in a database transaction. But if you mock it, how will you know if the code is working in such a way that it updates the Customer object?

I have the Answer.

The Answer

The Answer is literally the Answer.

As in: the Answer class. It specifies an action that occurs and a value that gets returned when a unit test case interacts with a mock.

Take a look at the test class to see what I'm chatting about:

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import us.careydevelopment.mockito.crudapp.harness.CustomerHarness;
import us.careydevelopment.mockito.crudapp.model.Customer;

@ExtendWith(MockitoExtension.class)
public class CustomerDaoTest {

	@Mock
	CustomerDao dao;
	
	@Test
	public void testUpdatePhoneNumber() {
		final String updatedPhoneNumber = "919-555-1214";
		
		Customer customer = CustomerHarness.createCustomer();
		
		doAnswer(invocation -> {
			String phoneNumber = (String)invocation.getArgument(0);
			Customer cust = (Customer)invocation.getArgument(1);
			
			cust.setPhoneNumber(phoneNumber);
			
			return null;
		}).when(dao).updatePhoneNumber(anyString(), any(Customer.class));
		
		dao.updatePhoneNumber(updatedPhoneNumber, customer);
		
		Assertions.assertEquals(updatedPhoneNumber, customer.getPhoneNumber());		
	}
}

I left the imports in place so you can see where some of those static methods, like doAnswer(), come from.

First, note that the DAO object is mocked up with the @Mock annotation. That means all the tests in this class ain't gonna be workin' with the real DAO. They'll be workin' with the mock.

The class only has a single method: testUpdatePhoneNumber(). There's quite a bit happening in there.

For starters, the method sets a String variable equal to the new phone number. 

Next, the code enlists the aid of a harness to instantiate a Customer object. Here's what that code looks like:

public class CustomerHarness {

	public static final Integer ID = 37;
	public static final String LAST_NAME = "Reesee";
	public static final String FIRST_NAME = "Beatrice";
	public static final String PHONE_NUMBER = "919-555-1212";
	
	public static Customer createCustomer() {
		Customer customer = new Customer();
		
		customer.setFirstName(FIRST_NAME);
		customer.setLastName(LAST_NAME);
		customer.setPhoneNumber(PHONE_NUMBER);
		customer.setId(ID);
		
		return customer;
	}

}

If you're paying close attention, you'll see that the unit test will change the customer's phone number from 919-555-1212 to 919-555-1214.

But back to the unit test code. What the heck is that doAnswer() thing?

That's the part that gives you your Answer. But notice that it doesn't operate by itself. There's a when() method chained to it.

That when() method tells the code when to execute the doAnswer() method. In the example above, it calls that method whenever the updatePhoneNumber() method on the mock gets called.

The values that get passed in to the updatePhoneNumber() method don't matter. That's why you see those any bits as method arguments.

Now take a look at what's happening in doAnswer().

The first two lines of code get the arguments passed in to the method. They have to be explicitly cast because they could be anything.

Also, the argument order uses 0-based indexing. So the first argument is argument #0, the second argument is argument #1, and so on.

Then the code sets the customer's phone number based on the values passed in.

Finally it returns null.

Now look at the last two lines in the test method.

The first one invokes the updatePhoneNumber() method on the mock. That will trigger the doAnswer() method.

The last line in the method is an assertion that checks to make sure the customer's phone number got updated.

Now run that thing and your test should pass with flying colors.

Wrapping It Up

Congrats! You now know how to test a method that returns void with Mockito.

Over to you. Take what you've learned here and use it in your real-world application.

Also, try to structure your application so doAnswer() uses non-testing code to produce the correct result. You might find some great opportunities for refactoring.

Have fun!

Photo by Jessica Lynn Lewis from Pexels