Introduction
Mockito provides ArgumentMatchers
to specify flexible argument constraints while verifying or stubbing method calls on mock objects. This is particularly useful when the exact values of arguments are not crucial, or when you want to apply conditions to the arguments. This tutorial will demonstrate how to use various ArgumentMatchers
in Mockito to handle flexible argument matching.
In Mockito, while writing test cases, there may arise situations where you don't care about the input argument of a method call; you just want the method to return a certain value or behave in a specific way regardless of what is passed to it. This is where ArgumentMatchers comes into play. They allow you to specify generic arguments that match certain criteria instead of specific values.
Maven Dependencies
To use Mockito with JUnit 5, add the following dependencies to your pom.xml
file:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.2</version>
<scope>test</scope>
</dependency>
Example Scenario
We will create a CustomerService
class that has a dependency on a CustomerRepository
. Our goal is to test the CustomerService
methods using various ArgumentMatchers
in Mockito to handle flexible argument matching.
CustomerService and CustomerRepository Classes
First, create the Customer
class, the CustomerRepository
interface, and the CustomerService
class.
public class Customer {
private String name;
private String email;
// Constructor, getters, and setters
public Customer(String name, String email) {
this.name = name;
this.email = email;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
public interface CustomerRepository {
void saveCustomer(Customer customer);
Customer findCustomerByEmail(String email);
List<Customer> findAllCustomers();
}
public class CustomerService {
private final CustomerRepository customerRepository;
public CustomerService(CustomerRepository customerRepository) {
this.customerRepository = customerRepository;
}
public void registerCustomer(String name, String email) {
Customer customer = new Customer(name, email);
customerRepository.saveCustomer(customer);
}
public Customer getCustomerByEmail(String email) {
return customerRepository.findCustomerByEmail(email);
}
public List<Customer> getAllCustomers() {
return customerRepository.findAllCustomers();
}
}
JUnit 5 Test Class with Mockito
Create a test class for CustomerService
using JUnit 5 and Mockito.
import static org.mockito.Mockito.*;
import static org.mockito.ArgumentMatchers.*;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;
import java.util.Arrays;
import java.util.List;
@ExtendWith(MockitoExtension.class)
public class CustomerServiceTest {
@Mock
private CustomerRepository customerRepository;
@InjectMocks
private CustomerService customerService;
@Test
public void testRegisterCustomer() {
// Given
String name = "Ramesh Fadatare";
String email = "ramesh.fadatare@example.com";
// When
customerService.registerCustomer(name, email);
// Then
verify(customerRepository).saveCustomer(any(Customer.class));
}
@Test
public void testGetCustomerByEmail() {
// Given
String email = "anita.patil@example.com";
Customer customer = new Customer("Anita Patil", email);
when(customerRepository.findCustomerByEmail(anyString())).thenReturn(customer);
// When
Customer result = customerService.getCustomerByEmail(email);
// Then
assertNotNull(result);
assertEquals("Anita Patil", result.getName());
assertEquals(email, result.getEmail());
}
@Test
public void testGetAllCustomers() {
// Given
Customer customer1 = new Customer("Ravi Kumar", "ravi.kumar@example.com");
Customer customer2 = new Customer("Rajesh Verma", "rajesh.verma@example.com");
List<Customer> customers = Arrays.asList(customer1, customer2);
when(customerRepository.findAllCustomers()).thenReturn(customers);
// When
List<Customer> result = customerService.getAllCustomers();
// Then
assertNotNull(result);
assertEquals(2, result.size());
assertEquals("Ravi Kumar", result.get(0).getName());
assertEquals("Rajesh Verma", result.get(1).getName());
}
@Test
public void testSaveCustomerWithCustomMatcher() {
// Given
String name = "Anjali Sharma";
String email = "anjali.sharma@example.com";
Customer customer = new Customer(name, email);
// When
customerService.registerCustomer(name, email);
// Then
verify(customerRepository).saveCustomer(argThat(argument -> argument.getName().equals("Anjali Sharma")));
}
}
Explanation
Creating Mocks with
@Mock
:- The
@Mock
annotation creates a mock instance of theCustomerRepository
interface. This mock instance can be used to simulate the behavior of theCustomerRepository
in a controlled way.
- The
Injecting Mocks with
@InjectMocks
:- The
@InjectMocks
annotation injects the mockCustomerRepository
into theCustomerService
instance to provide a controlled test environment. This allows theCustomerService
methods to be tested in isolation from the actualCustomerRepository
implementation.
- The
Using
ArgumentMatchers
:any()
: Theverify(customerRepository).saveCustomer(any(Customer.class));
method checks if thesaveCustomer
method was called on theCustomerRepository
with anyCustomer
object.anyString()
: Thewhen(customerRepository.findCustomerByEmail(anyString())).thenReturn(customer);
method configures the mockCustomerRepository
to return a specificCustomer
object when thefindCustomerByEmail
method is called with anyString
value.argThat()
: Theverify(customerRepository).saveCustomer(argThat(argument -> argument.getName().equals("Anjali Sharma")));
method checks if thesaveCustomer
method was called on theCustomerRepository
with aCustomer
object whose name is "Anjali Sharma".
Additional Scenarios
Scenario: Verifying Method Call with Multiple Argument Matchers
In this scenario, we will demonstrate how to verify a method call with multiple argument matchers using ArgumentMatchers
.
@Test
public void testSaveCustomerWithMultipleMatchers() {
// Given
String name = "Rajesh Verma";
String email = "rajesh.verma@example.com";
// When
customerService.registerCustomer(name, email);
// Then
verify(customerRepository).saveCustomer(argThat(argument -> argument.getName().equals("Rajesh Verma") && argument.getEmail().equals("rajesh.verma@example.com")));
}
Explanation
- Using Multiple Argument Matchers:
- The
verify(customerRepository).saveCustomer(argThat(argument -> argument.getName().equals("Rajesh Verma") && argument.getEmail().equals("rajesh.verma@example.com")));
method checks if thesaveCustomer
method was called on theCustomerRepository
with aCustomer
object whose name is "Rajesh Verma" and email is "rajesh.verma@example.com".
- The
Conclusion
ArgumentMatchers
in Mockito simplifies the verification and stubbing of method calls on mock objects for unit testing. By using ArgumentMatchers
, you can handle flexible argument matching, ensuring that your code interacts with its dependencies as expected. This step-by-step guide demonstrated how to effectively use ArgumentMatchers
in your unit tests, covering different scenarios to ensure comprehensive testing of the CustomerService
class.
Related Mockito Methods
Mockito mock()
Mockito spy()
Mockito when()
Mockito thenThrow()
Mockito verify()
Mockito times()
Mockito never()
Mockito any()
Mockito eq()
Mockito inOrder()
Mockito doReturn()
Mockito doThrow()
Mockito doAnswer()
Mockito timeout()
Mockito ArgumentMatchers
Comments
Post a Comment
Leave Comment