In order to simulate the behaviour of any argument of the specified type during unit testing, we can utilize Mockito argument matchers. In this tutorial, we’ll discuss an Introduction to the Mockito Argument Matchers along with some examples and the creation of a custom argument matcher.
So, let’s begin!
If you are not familiar with unit tests with JUnit and Mockito framework, it is recommended to go through our Testing Java Code tutorials first.
Mockito Argument Matchers
Mockito allows us to create mock objects and stub the behaviour for our test cases. It uses the when-then approach to instruct the mocked objects to behave in a particular way. Let’s discuss the concept of matchers and argument matchers in the next section!
What are Matchers?
The concept of Matchers is similar to regex or wildcards, except instead of specifying a single input (or output), we can specify a range or kind of input/output based on which stubs/spies can be built, and calls to stubs can be validated. Matchers are an effective tool that allows for quicker setup of stubs and the verification of invocations on the stubs by mentioning argument inputs as general types to particular values according to the use-case or scenario.
What are Argument Matchers?
Mockito Argument Matchers reside in the org.mockito.ArgumentMatchers class. The Mockito Argument Matcher methods are defined as static methods. Mockito offers a wide range of Argument Matchers for verification and matching of argument values of various data types. Internally, it uses it’s heritage Equal() method to verify and match parameter values.
Project Setup
Let’s assume you have a Maven, Spring Boot Project. Refer to this article for more details.
To begin with, add the following dependency to your Maven project:
<dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <version>4.7.0</version> <scope>test</scope> </dependency>
Let’s explore a few commonly used Mockito Argument Matchers!
Classification of ArgumentMatcher based on Data Types
Boolean
The argument matcher anyBoolean() matches to any boolean or not-null boolean values. The following is the code snippet of using Boolean Argument Matcher:
when(mockStudentService.getListOfPassedStudents(anyBoolean())).thenReturn(getStudentsList());
Character
The argument matcher anyChar() matches to any char or not-null character values. The following is the code snippet of using Character Argument Matcher:
when(mockStudentService.getStudentNamesStartingWithAlphabet(anyChar())).thenReturn(getStudentsList());
Double
The argument matcher anyDouble() matches to any double or not-null double values. The following is the code snippet of using Double Argument Matcher:
when(mockStudentService.calculateCoursePercentage(anyDouble(), anyLong())).thenReturn(getPercentage());
Float
The argument matcher anyFloat() matches to any float or not-null float values. The following is the code snippet of using Float Argument Matcher:
when(mockStudentService.calculateCoursePercentage(anyFloat(), anyLong())).thenReturn(getPercentage());
Integer
The argument matcher anyInt() matches to any int or not-null integer values. The following is the code snippet of using Integer Argument Matcher:
when(mockStudentService.getRegisteredCourses(anyInt())).thenReturn(getCourses());
Long
The argument matcher anyLong() matches to any long or not-null long values. The following is the code snippet of using Long Argument Matcher:
when(mockStudentService.calculateCoursePercentage(anyDouble(), anyLong())).thenReturn(getPercentage());
List
The argument matcher anyList() matches any not-null list. The following is the code snippet of using List Argument Matcher:
when(mockStudentService.getCountOfStudentsInASection(anyList())).thenReturn(50);
String
The argument matcher anyString() matches any not-null String. The following is the code snippet of using String Argument Matcher:
when(mockStudentService.getStudentById(anyString())).thenReturn(getStudent());
Matches Argument is equal to the Given Value
If we want to use a specific value for an argument, then we can use the eq() argument matcher. There are multiple variants available for the eq() method based on the data type.
The following is the code snippet of using eq() Argument Matcher for the Long data type:
when(mockStudentService.calculateCoursePercentage(anyDouble(), eq(100l))).thenReturn(50.0);
This would guarantee that the service would always return a value of 50.0 in accordance with our stubbed method criterion whenever a long argument of 100 is provided with the first parameter of any Double value.
Matches the String argument ends with the given Suffix Value
The argument matcher endsWith() matches the String argument that ends with the given suffix.
Custom Argument Matchers
We can also write a custom argument matcher for any other data type of our own choice. For example, in complex use cases, we write our own model classes and DTOs. To verify argument values, we must have to write our own custom piece of code.
For instance, we can have a StudentController that adds student data to the database by taking a StudentDTO as a request body. StudentService is responsible for mapping DTO to model class Student and finally persists it to the database. To verify the process goes smoothly, we will verify that StudentService is called exactly 1 time with any Student object. In this case, we will be utilizing any() argument matcher.
@Test public void testCustomArgumentMatcher() { StudentDTO studentDTO = new StudentDTO(); studentDTO.setId(1); studentDTO.setFirstName("Reham"); studentDTO.setLastName("Muzzamil"); studentController.addStudent(studentDTO); verify(studentService, times(1)).saveStudentData(any(Student.class)); }
Another use case is to verify the accuracy of DTO to entity object mapping. This helps us in ensuring that the data supplied via Student DTO is actually passed to a newly constructed Student Object. To validate this behaviour, we must have to write a custom argument matcher. The following is an example of defining our own custom argument matcher for the Student data type:
public class StudentMatcher implements ArgumentMatcher<Student> { private Student left; public StudentMatcher(Student left) { this.left = left; } public StudentMatcher() { } @Override public boolean matches(Student right) { return left.getId().equals(right.getId()) && left.getFirstName().equals(right.getFirstName()) && left.getLastName().equals(right.getLastName()); } }
To use the custom argument matcher defined above, we will write a new test case that not only verifies the interaction with the service but also verifies the data received by the StudentService. Here, we will use another argument matcher argThat():
@Test public void testCustomArgumentMatcherWithDataConversionVerification() { StudentDTO studentDTO = new StudentDTO(); studentDTO.setId(1); studentDTO.setFirstName("Reham"); studentDTO.setLastName("Muzzamil"); studentController.addStudent(studentDTO); Student student = new Student(); student.setId(1); student.setFirstName("Reham"); student.setLastName("Muzzamil"); verify(studentService, times(1)).saveStudentData(argThat(new StudentMatcher(student))); }
argThat() is an argument matcher that allows the creation of a custom argument matcher and matches an argument value accordingly.
How to combine multiple argument matchers?
Suppose you have a method saveStudent() in your StudentService class that takes a StudentDTO object as an argument and saves it to a database. You want to write a test case using Mockito to verify that the correct StudentDTO object is passed to this method. Specifically, you want to verify that the id field is greater than 0 and the firstName field starts with “Reham”.
Here’s how you could use argThat() to combine two argument matchers:
import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.Mockito.*; ... @Test public void testSaveStudent() { // Create a mock StudentService object StudentService studentService = mock(StudentService.class); // Create a StudentDTO object to pass to the saveStudent method StudentDTO studentDTO = new StudentDTO(1, "Reham", "Muzzamil"); // Set up the argument matcher for the id field ArgumentMatcher<Integer> idMatcher = id -> id > 0; // Set up the argument matcher for the firstName field ArgumentMatcher<String> firstNameMatcher = firstName -> firstName.startsWith("Reham"); // Combine the two argument matchers using argThat() ArgumentMatcher<StudentDTO> studentMatcher = argThat( student -> idMatcher.matches(student.getId()) && firstNameMatcher.matches(student.getFirstName()) ); // Call the saveStudent method with the StudentDTO object studentService.saveStudent(studentDTO); // Verify that the saveStudent method was called with the correct StudentDTO object verify(studentService).saveStudent(argThat(studentMatcher)); }
In this example, we use argThat() to combine the two argument matchers idMatcher and firstNameMatcher, into a single argument matcher studentMatcher. We then pass studentMatcher as the argument to the verify() method to check that the saveStudent method was called with a StudentDTO object that matches our criteria.
Note that we use the lambda expression syntax to define the argument matchers for the id and firstName fields, and we call the matches() method on each matcher inside the studentMatcher argument matcher to combine the two criteria. You can modify this example to match your specific requirements.
Can I use argument matchers with void methods?
Yes, you can use argument matchers with void methods by using the Mockito.verify() method instead of Mockito.when(). For example, to verify that a void method was called with a specific argument, you can use:
Mockito.verify(mockedObject).method(anyInt());
Suppose you have a method deleteStudent() in your StudentService class that takes a StudentDTO object as an argument and deletes it from the database. You want to write a test case using Mockito to verify that this method is called with a StudentDTO object that has an id greater than 0.
Here’s how you could use an argument matcher with a void method:
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.*; ... @Test public void testDeleteStudent() { // Create a mock StudentService object StudentService studentService = mock(StudentService.class); // Create a StudentDTO object to pass to the deleteStudent method StudentDTO studentDTO = new StudentDTO(1, "Reham", "Muzzamil"); // Call the deleteStudent method with the StudentDTO object studentService.deleteStudent(studentDTO); // Verify that the deleteStudent method was called with an argument that has an id greater than 0 verify(studentService).deleteStudent(argThat(student -> student.getId() > 0)); }
In this example, we use argThat() to create an argument matcher that matches any StudentDTO object with an id greater than 0. We pass this argument matcher to the verify() method, along with the mock StudentService object and the method call deleteStudent(studentDTO). This verifies that the deleteStudent() method was called with a StudentDTO object that matches our criteria.
Note that we use the lambda expression syntax to define the argument matcher for the id field inside the argThat() method. We also use anyInt() as a fallback matcher for the case where no StudentDTO object is passed to the deleteStudent() method. You can modify this example to match your specific requirements.
Conclusion
In this article, we have gone through the basics of Mockito Argument Matchers and saw many examples of different argument matchers being supported by the library. We have also covered an introduction to the creation of custom argument matchers along with the data verification of arguments. We have seen that Mockito offers a large number of Argument matchers that are mainly used for performing flexible verification and stubbing in Mockito.
Looking to master the art of testing in Java? Look no further than our Mockito tutorials featured on the Testing Java Code page. Uncover the secrets of mocking dependencies and writing concise yet comprehensive tests, ensuring the quality and reliability of your code.
Happy Learning!