1. Overview

In this tutorial, we’ll investigate how to capture arguments that are passed into mocked methods.

For this purpose, we’ll use the ArgumentCaptor class of Mockito. In the end, we’ll be able to capture arguments and write assertions against them.

2. Sample Application

Let’s first look at our sample application.

We’ll be using PersonService and PersonRepository classes. Notice that PersonService includes PersonRepository as a dependency.

public class PersonRepository {
...

    public void delete(Person person) {
        System.out.println("Deleting");
    }
}


public class PersonService {

    private final PersonRepository personRepository;

    public PersonService(PersonRepository personRepository) {
        this.personRepository = personRepository;
    }
...

    public void delete(Person person) {
        person.setName("deleted");
        personRepository.delete(person);
    }
}

3. ArgumentCaptor Usage

Now, we’ll look at the usage of ArgumentCaptor class which enables us to capture arguments.

Firstly, we should create an instance of ArgumentCaptor with appropriate type parameter. Then, we should call ArgumentCaptor.capture() during the verification phase of our test:

@InjectMocks
private PersonService personService;

@Mock
private PersonRepository personRepository;

@Captor
private ArgumentCaptor<Person> captor;

@Test
public void shouldCapture() {
    Person person = new Person("test");

    personService.delete(person);

    Mockito.verify(personRepository).delete(captor.capture());

    Person captured = captor.getValue();

    Assertions.assertThat(captured.getName()).isEqualTo("deleted");
}

Here, PersonService.delete() method sets the person’s name as “deleted”. To verify that name is indeed set as “deleted”, we’re capturing the Person argument given to PersonRepository.delete() method. Then we’re making our assertions.

There are other points to note here. Firstly, we’re declaring ArgumentCaptor with Person as the type parameter – ArgumentCaptor<Person>. Secondly, we’re capturing the value on verification phase – Mockito.verify(), not on expectation phase – Mockito.when(). And lastly, we’re getting the captured value with getValue() method.

4. Multiple Captures

Let’s continue with multiple captures.

Previously, we captured only one value, since there was one invocation. But we can also capture multiple values:

@InjectMocks
private PersonService personService;

@Mock
private PersonRepository personRepository;

@Captor
private ArgumentCaptor<Person> captor;

@Test
public void shouldCaptureMultipleTimes() {
    personService.delete(new Person("test"));
    personService.delete(new Person("test"));

    Mockito.verify(personRepository, Mockito.times(2)).delete(captor.capture());

    List<Person> allValues = captor.getAllValues();

    for (Person captured : allValues) {
        Assertions.assertThat(captured.getName()).isEqualTo("deleted");
    }
}

Here, we’re calling delete() method twice so 2 values are captured. Then we’re getting captured values with the getValues() method.

5. ArgumentCaptor Initialization

Next, let’s look at how we can initialize ArgumentCaptor instances.

5.1. Initializing ArgumentCaptor with @Captor

Firstly, we can use @Captor annotation:

@Captor
private ArgumentCaptor<Person> captor;

Here, we’re declaring an ArgumentCaptor<Person> and using @Captor annotation.

Let’s summarize when Mockito scans this annotation and initializes ArgumentCaptor. Firstly, we can run the test class with Mockito’s test runner – @RunWith(MockitoJUnitRunner.class). Secondly, we can call MockitoAnnotations.initMocks(this) in the test method. Lastly, we can use MockitoRule in the test class.

In our case, the test is run as @RunWith(MockitoJUnitRunner.class).

5.2. Initializing ArgumentCaptor with ArgumentCaptor.forClass()

We can also create an ArgumentCaptor without an annotation:

@InjectMocks
private PersonService personService;

@Mock
private PersonRepository personRepository;

@Test
public void shouldCaptureManually() {
    ArgumentCaptor<Person> argumentCaptor = ArgumentCaptor.forClass(Person.class);
    Person person = new Person("test");

    personService.delete(person);

    Mockito.verify(personRepository).delete(argumentCaptor.capture());

    Person captured = argumentCaptor.getValue();

    Assertions.assertThat(captured.getName()).isEqualTo("deleted");
}

Here, we’re using ArgumentCaptor.forClass(Person.class) to create one.

6. Summary

In this tutorial, we’ve looked at how to capture arguments using Mockito.

As always, the source code is available on Github.

Leave a Reply

Close Menu