If you're reading this, you're probably running some tests with Mockito and want to know how to verify that a method never got called.

And you're not sure how to do it, so you went to your favorite search engine which brought you here.

(Thank you, favorite search engine.)

Good news: I'll show you how to do that.

Yes, you can prove a negative.

The Use Case

Let's say you're developing an ecommerce application that allows customers to login with a name and password. When a user logs in successfully, that person's info is stored in the session.

But if the login fails, then nothing gets stored in the session.

So you want to write a unit test that verifies that no data gets stored in the session after a failed login attempt.

That's fine and dandy, but your simple little unit test doesn't have access to the HttpSession object. So instead you'd just like to verify that the method that stores user data in the session never gets called.

Got it? Okay, let's get started.

DAOing to Go out With You

The application uses the all-too-familiar data access object (DAO) pattern to handle authentication. But to keep things simple here, the code will perform an on-the-spot check:

public class CustomerDao {
	
	public Customer login(String username, String password) {
		if ("joe".equals(username) && "password".equals(password)) {
			Customer customer = new Customer();
			return customer;	
		}
		
		return null;
	}
}

So if the user logs in with the username "joe" and the password "password", then the application grants access. Otherwise, it returns null.

Also note that the login() method returns an empty Customer object in the event of a successful login. That's good enough for the purposes of this guide.

Normally, though, you'd use JPA to handle all of the authentication work with this type of solution.

First Service

Here's what the LoginService class looks like:

public class LoginService {
	
	private CustomerDao customerDao = new CustomerDao();

	public Customer login(String username, String password) {
		Customer customer = customerDao.login(username, password);
		if (customer != null) saveInSession(customer);
		
		return customer;
	}
	
	void saveInSession(Customer customer) {
		//logic to save the customer info in the session
	}
}

That class relies on the DAO that you just saw. In the code above, it's instantiating the DAO manually. But you'd probably use @Autowired for that purpose if you're using Spring.

Again: we're keeping things simple just to focus on the test.

The login() method delegates the actual login part to the DAO. In the event of a successful login, the customer's details get stored in the session via the saveInSession() method.

But that won't happen in the event of an unsuccessful login.

Why? Because the Customer object will be null. And the saveInSession() method only gets called if the Customer object is not null.

A Desire for Dependencies

Next, take a look at the POM file:

<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>

Hone in on the dependencies. You'll need them if you want to run the test code that you see here.

You might not need those exact versions, though. So if you're already using downlevel versions of the same dependencies, you're likely in good shape.

Testing Time

Now here's the code that performs the test:

@ExtendWith(MockitoExtension.class)
public class LoginServiceTest {

	@Spy
	LoginService loginService;

	@Test
	public void testUnsuccessfulLogin() {
		loginService.login("james", "password");
		Mockito.verify(loginService, Mockito.never()).saveInSession(Mockito.any());
	}	
}

Note that the code above uses a spy instead of a mock. That works fine here because there's no database or downstream service integration in the code itself. Plus you want to call the "real" methods here and not any stubbed methods.

The only method, testUnsuccessfulLogin(), handles the check to ensure that the saveInSession() method does not get called.

That method starts by calling the login() method on the spy using invalid credentials.

Then, in the second line, it checks to ensure that saveInSession() didn't get called.

How does it do that? With the aid of the Mockito.verify() method.

Verifying that Something Didn't Happen

Let's take a closer look at this line:

Mockito.verify(loginService, Mockito.never()).saveInSession(Mockito.any());

The Mockito.verify() method (or just verify() if you opt for static imports) is used to verify that a method did or did not get called on an observed object.

The verify() method accepts two parameters. This first is the mock or spy that's going to get checked. Here, it's loginService

That makes sense because you want to see if the saveInSession() method got called and that method is in the LoginService class.

But since you want to make sure that the method never gets called, you need a second parameter. It's a VerificationMode object.

That object is used to verify that a certain action occurred a number of times. But that number could be 0. 

And that's what Mockito.never() is checking. To ensure that the method got called 0 times.

The method name is specified after the second period. And it's using Mockito.any() as its only parameter because you want to perform the check no matter what value (including null) gets passed in there.

And there you have it. That's the test.

Now run it and you should be in good shape.

Wrapping It Up

Bingo! Now you know how to test that a method never got called when running unit tests with Mockito.

But you're going to need to take what you've learned here and put it in your own test cases. Feel free to tinker as needed to make that happen.

Have fun!

Photo by RODNAE Productions from Pexels