Mockito

Mockito Verify Example

In this article, I am going to show you an example of Mockito Verify. To test the state, we use assert, likewise, to verify the test interactions, we use Mockito.verify.

Below are my setup details:

Let’s start verifying behavior!

1. System Under Test (SUT)

A test consists of the following three steps:

  1. Stubbing
  2. Running the SUT
  3. Verifying the behavior of SUT

In this example, the system under test is a Customer who wants to withdraw some money. It has method withdraw(amount) which collaborates with an AccountManager to validate whether the customer has enough funds to withdraw.

If the customer has enough funds, the account manager will allow withdrawing money and return us the new balance. If the funds are not enough, it will throw NotEnoughFundsException.

Ok, we know our SUT, we need to know the class we are going to stub. Well…can you make a guess? It is AccountManager. In this example, we will stub its methods and verify how our SUT behaves in each case.

Before we start with our test cases, let’s go through each class.

The first one is an Account class. It is empty, as the actual processing is handled only in AccountManager

Account:

package com.javacodegeeks.mockito;

public class Account {
}

Next is Customer class. We already know its behavior but I just thought of adding couple of more points here:

  1. Customer relies on AccountManager for withdrawing the amount. It has method setAccountManager(AccountManager) which we will use to set the mock object
  2. withdraw(amount) throws NotEnoughFundsException if funds are not enough. Else it will return the new balance after the withdrawal process.

Customer:

package com.javacodegeeks.mockito;

public class Customer {
	private AccountManager accountManager;

	public long withdraw(long amount) throws NotEnoughFundsException {
		Account account = accountManager.findAccount(this);
		long balance = accountManager.getBalance(account);
		if (balance < amount) {
			throw new NotEnoughFundsException();
		}
		accountManager.withdraw(account, amount);
		return accountManager.getBalance(account);
	}

	public void setAccountManager(AccountManager accountManager) {
		this.accountManager = accountManager;
	}
}

NotEnoughFundsException:

package com.javacodegeeks.mockito;

public class NotEnoughFundsException extends Exception {
	private static final long serialVersionUID = 1L;
}

AccountManager is responsible for managing the funds. Its methods are self-explanatory.

AccountManager:

package com.javacodegeeks.mockito;

public interface AccountManager {

	long getBalance(Account account);

	long withdraw(Account account, long amount);

	Account findAccount(Customer customer);

}

2. Verify Behavior

The first test case is withdrawButNotEnoughFunds. We will try to withdraw more amount than is allowed. In the @BeforeMethod called setupMock(), we create the Customer object, mock AccountManager and set it to the Customer. We stub the accountManager.findAccount(customer) to return Account object.

Few points to note about the test case:

  1. Stub AccountManager to return balance lesser than the amount requested.
    when(accountManager.getBalance(account)).thenReturn(balanceAmount200);
  2. Run the SUT method Customer.withdraw(2000)
  3. Assert using expectedExceptions attribute that NotEnoughFundsException is thrown
  4. Verify that certain methods from the mock object are called.
  5. Verify that accountManager.findAccount(customer) was called.
    verify(accountManager).findAccount(customer)
  6. Verify that accountManager.withdraw(account, amount) was never called.
    verify(accountManager, times(0)).withdraw(account, withdrawlAmount2000);

MockitoVerifyExample:

package com.javacodegeeks.mockito;

import static org.mockito.Mockito.*;

import org.mockito.InOrder;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class MockitoVerifyExample {
	private Customer customer;
	private AccountManager accountManager;
	private Account account;
	private long withdrawlAmount2000 = 2000L;
	
	@BeforeMethod
	public void setupMock() {
		customer = new Customer();
		accountManager = mock(AccountManager.class);
		customer.setAccountManager(accountManager);
		account = mock(Account.class);
		when(accountManager.findAccount(customer)).thenReturn(account);		
	}
	
	@Test(expectedExceptions=NotEnoughFundsException.class)
	public void withdrawButNotEnoughFunds() throws NotEnoughFundsException {
		long balanceAmount200 = 200L;
		
		p("Train getBalance(account) to return " + balanceAmount200);
		when(accountManager.getBalance(account)).thenReturn(balanceAmount200);
		
		printBalance(balanceAmount200);
		try {
			p("Customer.withdraw(" + withdrawlAmount2000 + ") should fail with NotEnoughFundsException");
			customer.withdraw(withdrawlAmount2000);			
		} catch (NotEnoughFundsException e) {
			p("NotEnoughFundsException is thrown"); 
			
			verify(accountManager).findAccount(customer);
			p("Verified findAccount(customer) is called");			
			
			verify(accountManager, times(0)).withdraw(account, withdrawlAmount2000);
			p("Verified withdraw(account, " + withdrawlAmount2000 + ") is not called");
			
			throw e;
		}
	}

	private static void p(String text) {
		System.out.println(text);
	}
	
	private void printBalance(long balance) {
		p("Balance is " + balance + " and withdrawl amount " + withdrawlAmount2000);	
	}
}

Output:

Train getBalance(account) to return 200
Balance is 200 and withdrawl amount 2000
Customer.withdraw(2000) should fail with NotEnoughFundsException
NotEnoughFundsException is thrown
Verified findAccount(customer) is called
Verified withdraw(account, 2000) is not called
PASSED: withdrawButNotEnoughFunds

3. Verification by count

In the next example of Mockito Verify, we will review the test case withdrawal() which defines the success scenario. Few points to note about the test case:

  1. We stub accountManager.getBalance(customer) to return enough balance for a successful withdrawal.
  2. Since withdrawal was successful, we verify that accountManager.withdraw(account, amount) was called.
    verify(accountManager).withdraw(account, withdrawlAmount2000);
    
  3. We also verify the number of times a method was called. For example, in the case of successful withdrawal, we end up calling accountManager.getBalance(account) twice. Once before the withdrawal and the second time after the withdrawal.
    verify(accountManager, times(2)).getBalance(account)
  4. Account is determined only once.
    verify(accountManager, atLeastOnce()).findAccount(customer);

MockitoVerifyExample:

package com.javacodegeeks.mockito;

import static org.mockito.Mockito.*;

import org.mockito.InOrder;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class MockitoVerifyExample {
	private Customer customer;
	private AccountManager accountManager;
	private Account account;
	private long withdrawlAmount2000 = 2000L;
	
	@BeforeMethod
	public void setupMock() {
		customer = new Customer();
		accountManager = mock(AccountManager.class);
		customer.setAccountManager(accountManager);
		account = mock(Account.class);
		when(accountManager.findAccount(customer)).thenReturn(account);		
	}
	
	@Test(expectedExceptions=NotEnoughFundsException.class)
	public void withdrawButNotEnoughFunds() throws NotEnoughFundsException {
		long balanceAmount200 = 200L;
		
		p("Train getBalance(account) to return " + balanceAmount200);
		when(accountManager.getBalance(account)).thenReturn(balanceAmount200);
		
		printBalance(balanceAmount200);
		try {
			p("Customer.withdraw(" + withdrawlAmount2000 + ") should fail with NotEnoughFundsException");
			customer.withdraw(withdrawlAmount2000);			
		} catch (NotEnoughFundsException e) {
			p("NotEnoughFundsException is thrown"); 
			
			verify(accountManager).findAccount(customer);
			p("Verified findAccount(customer) is called");			
			
			verify(accountManager, times(0)).withdraw(account, withdrawlAmount2000);
			p("Verified withdraw(account, " + withdrawlAmount2000 + ") is not called");
			
			throw e;
		}
	}
	
	@Test
	public void withdraw() throws NotEnoughFundsException {		
		long balanceAmount3000 = 3000L;
		
		p("Train getBalance(account) to return " + balanceAmount3000);
		when(accountManager.getBalance(account)).thenReturn(balanceAmount3000);
		
		printBalance(balanceAmount3000);
		
		p("Customer.withdraw(" + withdrawlAmount2000 + ")");
		customer.withdraw(withdrawlAmount2000);
		
		verify(accountManager, times(2)).getBalance(account);
		p("Verified getBalance(account) is called twice");
		
		verify(accountManager).withdraw(account, withdrawlAmount2000);
		p("Verified withdraw(account, " +  withdrawlAmount2000 + ") is called just once");
		
		verify(accountManager, atLeastOnce()).findAccount(customer);
		p("Verified findAccount(account) is called atleast once");
	}

	private static void p(String text) {
		System.out.println(text);
	}
	
	private void printBalance(long balance) {
		p("Balance is " + balance + " and withdrawl amount " + withdrawlAmount2000);	
	}	
}

Output:

Train getBalance(account) to return 3000
Balance is 3000 and withdrawl amount 2000
Customer.withdraw(2000)
Verified getBalance(account) is called twice
Verified withdraw(account, 2000) is called just once
Verified findAccount(account) is called atleast once
PASSED: withdraw

4. Verify Order

In our next test case withdrawAndVerifyOrder, we verify the order in which methods were called using inOrder(). To enforce the order verification, we need to call our verify() methods on the InOrder object.

order.verify(accountManager).findAccount(customer);
InOrder order = inOrder(accountManager);

MockitoVerifyExample:

package com.javacodegeeks.mockito;

import static org.mockito.Mockito.*;

import org.mockito.InOrder;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class MockitoVerifyExample {
	private Customer customer;
	private AccountManager accountManager;
	private Account account;
	private long withdrawlAmount2000 = 2000L;
	
	@BeforeMethod
	public void setupMock() {
		customer = new Customer();
		accountManager = mock(AccountManager.class);
		customer.setAccountManager(accountManager);
		account = mock(Account.class);
		when(accountManager.findAccount(customer)).thenReturn(account);		
	}
	
	@Test(expectedExceptions=NotEnoughFundsException.class)
	public void withdrawButNotEnoughFunds() throws NotEnoughFundsException {
		long balanceAmount200 = 200L;
		
		p("Train getBalance(account) to return " + balanceAmount200);
		when(accountManager.getBalance(account)).thenReturn(balanceAmount200);
		
		printBalance(balanceAmount200);
		try {
			p("Customer.withdraw(" + withdrawlAmount2000 + ") should fail with NotEnoughFundsException");
			customer.withdraw(withdrawlAmount2000);			
		} catch (NotEnoughFundsException e) {
			p("NotEnoughFundsException is thrown"); 
			
			verify(accountManager).findAccount(customer);
			p("Verified findAccount(customer) is called");			
			
			verify(accountManager, times(0)).withdraw(account, withdrawlAmount2000);
			p("Verified withdraw(account, " + withdrawlAmount2000 + ") is not called");
			
			throw e;
		}
	}
	
	@Test
	public void withdraw() throws NotEnoughFundsException {		
		long balanceAmount3000 = 3000L;
		
		p("Train getBalance(account) to return " + balanceAmount3000);
		when(accountManager.getBalance(account)).thenReturn(balanceAmount3000);
		
		printBalance(balanceAmount3000);
		
		p("Customer.withdraw(" + withdrawlAmount2000 + ")");
		customer.withdraw(withdrawlAmount2000);
		
		verify(accountManager).withdraw(account, withdrawlAmount2000);
		p("Verified withdraw(account, " + withdrawlAmount2000 + ") is Called");
		
		verify(accountManager, times(2)).getBalance(account);
		p("Verified getBalance(account) is called twice");
		
		verify(accountManager).withdraw(account, withdrawlAmount2000);
		p("Verified withdraw(account, " +  withdrawlAmount2000 + ") is called just once");
		
		verify(accountManager, atLeastOnce()).findAccount(customer);
		p("Verified findAccount(account) is called atleast once");
	}
	
	@Test
	public void withdrawAndVerifyOrder() throws NotEnoughFundsException {	
		long balanceAmount3000 = 3000L;
		
		p("Train getBalance(account) to return " + balanceAmount3000);
		when(accountManager.getBalance(account)).thenReturn(balanceAmount3000);
		
		printBalance(balanceAmount3000);
		
		p("Customer.withdraw(" + withdrawlAmount2000 + ")");
		customer.withdraw(withdrawlAmount2000);
		
		p("Verify order of method calls");
		InOrder order = inOrder(accountManager);
		
		order.verify(accountManager).findAccount(customer);
		p("Verified findAccount(account) is called");
		
		order.verify(accountManager).getBalance(account);
		p("Verified getBalance(account) is called");
		
		order.verify(accountManager).withdraw(account, withdrawlAmount2000);
		p("Verified withdraw(account, " +  withdrawlAmount2000 + ") is called");
		
		order.verify(accountManager).getBalance(account);
		p("Verified getBalance(account) is called one more time after withdrawl");
	}
	
	private static void p(String text) {
		System.out.println(text);
	}
	
	private void printBalance(long balance) {
		p("Balance is " + balance + " and withdrawl amount " + withdrawlAmount2000);	
	}
}

Output:

Train getBalance(account) to return 3000
Balance is 3000 and withdrawl amount 2000
Customer.withdraw(2000)
Verify order of method calls
Verified findAccount(account) is called
Verified getBalance(account) is called
Verified withdraw(account, 2000) is called
Verified getBalance(account) is called one more time after withdrawl
PASSED: withdrawAndVerifyOrder

5. Unverified Interaction

In our last example, we will improve our previous test case withdrawAndVerifyOrder(). We will call verifyNoMoreInteractions(accountManager) in the end after verifying all the methods to make sure that nothing else was invoked on your mocks.

MockitoVerifyExample:

package com.javacodegeeks.mockito;

import static org.mockito.Mockito.*;

import org.mockito.InOrder;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

public class MockitoVerifyExample {
	private Customer customer;
	private AccountManager accountManager;
	private Account account;
	private long withdrawlAmount2000 = 2000L;
	
	@BeforeMethod
	public void setupMock() {
		customer = new Customer();
		accountManager = mock(AccountManager.class);
		customer.setAccountManager(accountManager);
		account = mock(Account.class);
		when(accountManager.findAccount(customer)).thenReturn(account);		
	}
	
	@Test(expectedExceptions=NotEnoughFundsException.class)
	public void withdrawButNotEnoughFunds() throws NotEnoughFundsException {
		long balanceAmount200 = 200L;
		
		p("Train getBalance(account) to return " + balanceAmount200);
		when(accountManager.getBalance(account)).thenReturn(balanceAmount200);
		
		printBalance(balanceAmount200);
		try {
			p("Customer.withdraw(" + withdrawlAmount2000 + ") should fail with NotEnoughFundsException");
			customer.withdraw(withdrawlAmount2000);			
		} catch (NotEnoughFundsException e) {
			p("NotEnoughFundsException is thrown"); 
			
			verify(accountManager).findAccount(customer);
			p("Verified findAccount(customer) is called");			
			
			verify(accountManager, times(0)).withdraw(account, withdrawlAmount2000);
			p("Verified withdraw(account, " + withdrawlAmount2000 + ") is not called");
			
			throw e;
		}
	}
	
	@Test
	public void withdraw() throws NotEnoughFundsException {		
		long balanceAmount3000 = 3000L;
		
		p("Train getBalance(account) to return " + balanceAmount3000);
		when(accountManager.getBalance(account)).thenReturn(balanceAmount3000);
		
		printBalance(balanceAmount3000);
		
		p("Customer.withdraw(" + withdrawlAmount2000 + ")");
		customer.withdraw(withdrawlAmount2000);
		
		verify(accountManager).withdraw(account, withdrawlAmount2000);
		p("Verified withdraw(account, " + withdrawlAmount2000 + ") is Called");
		
		verify(accountManager, times(2)).getBalance(account);
		p("Verified getBalance(account) is called twice");
		
		verify(accountManager).withdraw(account, withdrawlAmount2000);
		p("Verified withdraw(account, " +  withdrawlAmount2000 + ") is called just once");
		
		verify(accountManager, atLeastOnce()).findAccount(customer);
		p("Verified findAccount(account) is called atleast once");
	}
	
	@Test
	public void withdrawAndVerifyOrder() throws NotEnoughFundsException {	
		long balanceAmount3000 = 3000L;
		
		p("Train getBalance(account) to return " + balanceAmount3000);
		when(accountManager.getBalance(account)).thenReturn(balanceAmount3000);
		
		printBalance(balanceAmount3000);
		
		p("Customer.withdraw(" + withdrawlAmount2000 + ")");
		customer.withdraw(withdrawlAmount2000);
		
		p("Verify order of method calls");
		InOrder order = inOrder(accountManager);
		
		order.verify(accountManager).findAccount(customer);
		p("Verified findAccount(account) is called");
		
		order.verify(accountManager).getBalance(account);
		p("Verified getBalance(account) is called");
		
		order.verify(accountManager).withdraw(account, withdrawlAmount2000);
		p("Verified withdraw(account, " +  withdrawlAmount2000 + ") is called");
		
		order.verify(accountManager).getBalance(account);
		p("Verified getBalance(account) is called one more time after withdrawl");
		
		verifyNoMoreInteractions(accountManager);
		p("verified no more calls are executed on the mock object");
	}
	
	private static void p(String text) {
		System.out.println(text);
	}
	
	private void printBalance(long balance) {
		p("Balance is " + balance + " and withdrawl amount " + withdrawlAmount2000);	
	}
}

Output:

Train getBalance(account) to return 3000
Balance is 3000 and withdrawl amount 2000
Customer.withdraw(2000)
Verify order of method calls
Verified findAccount(account) is called
Verified getBalance(account) is called
Verified withdraw(account, 2000) is called
Verified getBalance(account) is called one more time after withdrawl
verified no more calls are executed on the mock object
PASSED: withdrawAndVerifyOrder

6. Download the Eclipse Project

This was an example of Mockito Verify.

Download
You can download the full source code of this example here: mockitoVerify.zip

Ram Mokkapaty

Ram holds a master's degree in Machine Design from IT B.H.U. His expertise lies in test driven development and re-factoring. He is passionate about open source technologies and actively blogs on various java and open-source technologies like spring. He works as a principal Engineer in the logistics domain.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button