ArgumentCaptor in Mockito
0 CommentsLast Updated on February 3, 2021 by jt
ArgumentCaptor in Mockito allows you to capture arguments passed to methods for further assertions. You can apply standard JUnit assertion methods, such as assertEquals(), assertThat(), and so on, to perform assertions on the captured arguments. In Mockito, you will find the ArgumentCaptor class in the org. mockito package
If you are new to mocking with Mockito, I suggest you go through my earlier post Mocking in Unit Tests with Mockito
In this post, I will explain how to create an ArgumentCaptor, its important methods, and how to use them.
Dependency
To use Mockito, you’ll need to add the following dependency in your pom.xml.
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.23.4</version>
<scope>test</scope>
</dependency>
Sample Application
The sample application is a simple Spring Boot application. It has a Student entity having id and name as its properties.
You can find the source code of the sample application here on Github.
The code of the Student entity class is this.
Student.java
@Entity
public class Student {
@Id
private int id;
private String name;
public Student() {
}
public Student(int id, String name) {
Id = id;
this.name = name;
}
public int getId() {
return Id;
}
public void setId(int id) {
Id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{ Id = " + Id + ", name = '" + name + '\'' + '}';
}
}
The application also have a service implementation to save Student entities and a Spring Data JPA repository interface.
The code of the StudentRepository interface is this.
StudentRepository.java
package springframework.repository;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import springframework.domain.Student;
@Repository
public interface StudentRepository extends CrudRepository<Student, Integer> {
}
The code of the StudentService interface is this.
StudentService.java
package springframework.service;
import springframework.domain.Student;
public interface StudentService {
Student saveStudent(Student student);
}
The implementation class is this.
StudentServiceImpl.java
package springframework.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import springframework.domain.Student;
import springframework.repository.StudentRepository;
@Service
public class StudentServiceImpl implements StudentService {
private StudentRepository studentRepository;
@Autowired
public StudentServiceImpl(StudentRepository studentRepository) {
this.studentRepository = studentRepository;
}
@Override
public Student saveStudent(Student student) {
return studentRepository.save(student);
}
}
Setting up the Unit Test
I have created a test class ArgumentCaptorTest.java. The code is this
@RunWith(MockitoJUnitRunner.class)
public class ArgumentCaptorTest {
@Captor
private ArgumentCaptor<Student> captor;
@Mock
private StudentRepository studentRepository;
@InjectMocks
private StudentServiceImpl studentService;
In the code above, Line 1 runs the test class with @RunWith(MockitoJUnitRunner.class) to make Mockito detect ArgumentCaptor, which we will declare next.
Line 2 – Line 3 creates an ArgumentCaptor of type Student and annotates it with @Captor to store captured argument.
Line 7 – Line 11 uses the @Mock annotation to mock StudentRepository, which is then automatically injected into our StudentServiceImpl with the @InjectMocks annotation.
Test Case to show ArgumentCapture Usage
Now, I will show the usage of ArgumentCaptor. This is the code.
@Test
public void shouldCapture() {
Student student1 = new Student(1, "Harry");
studentService.saveStudent(student1);
Mockito.verify(studentRepository).save(captor.capture());
assertEquals("Harry", captor.getValue().getName());
assertEquals(1,captor.getValue().getId());
}
To capture the method arguments, you need to use the capture() method of ArgumentCaptor. You should call it during the verification phase of the test.
In the code provided above, Line 4 – Line 5 creates and saves a Student object student1.
Line 7 calls Mockito.verify() to verify if the save() method of the mocked StudentRepository has been called. Then the call to captor.capture() captures the method argument passed to the mock method.
Line 9 – Line 10 perform assertions by calling the getValue() method of ArgumentCaptor to get the captured value of the argument.
Note: If the verified methods are called multiple times, then the getValue() method will return the latest captured value.
Now, let’s run our test.
This figure shows that the test case has passed successfully.

Multiple Captures using ArgumentCaptor
In the previous test case, we captured only one value, since there was only one verify method. To capture multiple argument values, ArgumentCaptor provides the getAllValues() method.
The test code is this.
@Test
public void shouldCaptureMultipleTimes() {
Student student1 = new Student(1, "Harry");
Student student2 = new Student(2, "Tae");
Student student3 = new Student(3, "Louis");
studentService.saveStudent(student1);
studentService.saveStudent(student2);
studentService.saveStudent(student3);
Mockito.verify(studentRepository,Mockito.times(3)).save(captor.capture());
List studentList = captor.getAllValues();
assertEquals("Harry", studentList.get(0).getName());
assertEquals("Tae", studentList.get(1).getName());
assertEquals("Louis", studentList.get(2).getName());
}
In the code provided above, Line 4 – Line 10 creates and saves three Student objects named student1, student2, and student3.
Then, Line 12 calls the Mockito.verify() method to check that the StudentRepository.save() is called thrice. Additionally, the call to captor.capture() captures the three values.
Line 14 obtains the list of captured values by the getValues() method. If you call verify methods multiple times, the getAllValue() method will return the merged list of all the values from all invocations.
Finally, the assertEquals() methods perform assertions on the captured arguments.
The output of the test is this.
The ArgumentCaptor.forClass() Method
Till now we have used the @Captor annotation to instantiate ArgumentCaptor in our test. Alternatively, you can use the ArgumentCaptor.forClass() method for the same purpose.
The test code is this.
ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Student.class);
@Test
public void shouldCaptureManually() {
Student student1 = new Student(1, "Harry");
studentService.saveStudent(student1);
Mockito.verify(studentRepository).save(argumentCaptor.capture());
Student captured = argumentCaptor.getValue();
assertEquals("Harry", captured.getName());
}
On running the test, you should see the test passing successfully.
Summary
I have seen test code using ArgumentCaptor with stubbing, an approach that I don’t advocate.
Let’s say, you use ArgumentCaptor during stubbing with Mockito.when, like this.
UserRegistration userRegistration = new UserRegistration ("Jammie", "[email protected]", 23);
Mockito.when(userRegistrationService.registerUser(registrationCaptor.capture())).thenReturn(userRegistration);
assertTrue(userService.register(userRegistration));
assertEquals(userRegistration, registrationCaptor.getValue());
This code decreases readability as compared to the conventional way of using Mockito.eq(userRegistration). Additionally, the call to registrationCaptor.capture() during stubbing lack clarity of its intent. Also, you end up with an extra assertion – the final assertEuals(), which you wouldn’t have required with the conventional Mockito.eq(userRegistration).
In addition, suppose userService.register() doesn’t call userRegistrationService.register() in the actual code. In this scenario, you will get this exception
org.mockito.exceptions.base.MockitoException: No argument value was captured!
This exception message is confusing and can trip you to believing that you have issues in your test. However, the issue is in the code you are testing and not in your test.
So the best practice is to use ArgumentCaptor the way it is designed for – during verification.
You can find the source code of this post on Github.
For in-depth knowledge on Testing in Spring Boot Applications check my Udemy Best Seller Course Testing Spring Boot: Beginner to Guru.



