Expand Authors Top

If you have a few years of experience in the Java ecosystem and you’d like to share that with the community, have a look at our Contribution Guidelines.

November Discount Launch 2022 – Top
We’re finally running a Black Friday launch. All Courses are 30% off until next Friday:

>> GET ACCESS NOW

November Discount Launch 2022 – TEMP TOP (NPI)
We’re finally running a Black Friday launch. All Courses are 30% off until next Friday:

>> GET ACCESS NOW

Expanded Audience – Frontegg – Security (partner)
announcement - icon User management is very complex, when implemented properly. No surprise here.

Not having to roll all of that out manually, but instead integrating a mature, fully-fledged solution - yeah, that makes a lot of sense.
That's basically what Frontegg is - User Management for your application. It's focused on making your app scalable, secure and enjoyable for your users.
From signup to authentication, it supports simple scenarios all the way to complex and custom application logic.

Have a look:

>> Elegant User Management, Tailor-made for B2B SaaS

1. Overview

In this tutorial, we'll cover a common use case of using Mockito ArgumentCaptor in our unit tests.

Alternatively, for other Mockito.verify use cases, see our Mockito Verify Cookbook.

Further reading:

Introduction to Mockito's AdditionalAnswers

A quick and practical guide to Mockito's AdditionalAnswers.

Mockito ArgumentMatchers

Learn how to use the ArgumentMatcher and how it differs from the ArgumentCaptor.

Mockito Verify Cookbook

Mockito Verify examples, usage and best practices.

2. Using ArgumentCaptor

ArgumentCaptor allows us to capture an argument passed to a method to inspect it. This is especially useful when we can't access the argument outside of the method we'd like to test.

For example, consider an EmailService class with a send method that we'd like to test:

public class EmailService {

    private DeliveryPlatform platform;

    public EmailService(DeliveryPlatform platform) {
        this.platform = platform;
    }

    public void send(String to, String subject, String body, boolean html) {
        Format format = Format.TEXT_ONLY;
        if (html) {
            format = Format.HTML;
        }
        Email email = new Email(to, subject, body);
        email.setFormat(format);
        platform.deliver(email);
    }

    ...
}

In EmailService.send, notice how platform.deliver takes a new Email as an argument. As part of our test, we'd like to check that the format field of the new Email is set to Format.HTML. To do this, we need to capture and inspect the argument that is passed to platform.deliver.

Let's see how we can use ArgumentCaptor to help us.

2.1. Set Up the Unit Test

First, let's create our unit test class:

@RunWith(MockitoJUnitRunner.class)
public class EmailServiceUnitTest {

    @Mock
    DeliveryPlatform platform;

    @InjectMocks
    EmailService emailService;
  
    ...
}

We're using the @Mock annotation to mock DeliveryPlatform, which is automatically injected into our EmailService with the @InjectMocks annotation. Refer to our Mockito annotations article for further details.

2.2. Add an ArgumentCaptor Field

Second, let's add a new ArgumentCaptor field of type Email to store our captured argument:

@Captor
ArgumentCaptor<Email> emailCaptor;

2.3. Capture the Argument

Third, let's use Mockito.verify with the ArgumentCaptor to capture the Email:

Mockito.verify(platform).deliver(emailCaptor.capture());

We can then get the captured value and store it as a new Email object:

Email emailCaptorValue = emailCaptor.getValue();

2.4. Inspect the Captured Value

Finally, let's see the whole test with an assert to inspect the captured Email object:

@Test
public void whenDoesSupportHtml_expectHTMLEmailFormat() {
    String to = "[email protected]";
    String subject = "Using ArgumentCaptor";
    String body = "Hey, let'use ArgumentCaptor";

    emailService.send(to, subject, body, true);

    Mockito.verify(platform).deliver(emailCaptor.capture());
    Email value = emailCaptor.getValue();
    assertThat(value.getFormat()).isEqualTo(Format.HTML);
}

3. Avoiding Stubbing

Although we can use an ArgumentCaptor with stubbing, we should generally avoid doing so. To clarify, in Mockito, this generally means avoiding using an ArgumentCaptor with Mockito.when. With stubbing, we should use an ArgumentMatcher instead.

Let's look at a couple of reasons why we should avoid stubbing.

3.1. Decreased Test Readability

First, consider a simple test:

Credentials credentials = new Credentials("baeldung", "correct_password", "correct_key");
Mockito.when(platform.authenticate(Mockito.eq(credentials)))
  .thenReturn(AuthenticationStatus.AUTHENTICATED);

assertTrue(emailService.authenticatedSuccessfully(credentials));

Here we use Mockito.eq(credentials) to specify when the mock should return an object.

Next, consider the same test using an ArgumentCaptor instead:

Credentials credentials = new Credentials("baeldung", "correct_password", "correct_key");
Mockito.when(platform.authenticate(credentialsCaptor.capture()))
  .thenReturn(AuthenticationStatus.AUTHENTICATED);

assertTrue(emailService.authenticatedSuccessfully(credentials));
assertEquals(credentials, credentialsCaptor.getValue());

In contrast to the first test, notice how we have to perform an extra assert on the last line to do the same as Mockito.eq(credentials).

Finally, notice how it isn't immediately clear what credentialsCaptor.capture() refers to. This is because we have to create the captor outside the line we use it on, reducing readability.

3.2. Reduced Defect Localization

Another reason is that if emailService.authenticatedSuccessfully doesn't call platform.authenticate, we'll get an exception:

org.mockito.exceptions.base.MockitoException: 
No argument value was captured!

This is because our stubbed method hasn't captured an argument. However, the real issue is not in our test itself but in the actual method we are testing.

In other words, it misdirects us to an exception in the test, whereas the actual defect is in the method we are testing.

4. Conclusion

In this short article, we looked at a general use case of using ArgumentCaptor. We also looked at the reasons for avoiding using ArgumentCaptor with stubbing.

As usual, all of our code samples are available over on GitHub.

November Discount Launch 2022 – Bottom
We’re finally running a Black Friday launch. All Courses are 30% off until next Friday:

>> GET ACCESS NOW

Junit footer banner
Comments are closed on this article!