[java] How to capture a list of specific type with mockito

Is there a way to capture a list of specific type using mockitos ArgumentCaptore. This doesn't work:

ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(ArrayList.class);

This question is related to java unit-testing junit mockito

The answer is


The nested generics-problem can be avoided with the @Captor annotation:

public class Test{

    @Mock
    private Service service;

    @Captor
    private ArgumentCaptor<ArrayList<SomeType>> captor;

    @Before
    public void init(){
        MockitoAnnotations.initMocks(this);
    }

    @Test 
    public void shouldDoStuffWithListValues() {
        //...
        verify(service).doStuff(captor.capture()));
    }
}

For an earlier version of junit, you can do

Class<Map<String, String>> mapClass = (Class) Map.class;
ArgumentCaptor<Map<String, String>> mapCaptor = ArgumentCaptor.forClass(mapClass);

If you're not afraid of old java-style (non type safe generic) semantics, this also works and is simple'ish:

ArgumentCaptor<List> argument = ArgumentCaptor.forClass(List.class);
verify(subject.method(argument.capture()); // run your code
List<SomeType> list = argument.getValue(); // first captured List, etc.

Yeah, this is a general generics problem, not mockito-specific.

There is no class object for ArrayList<SomeType>, and thus you can't type-safely pass such an object to a method requiring a Class<ArrayList<SomeType>>.

You can cast the object to the right type:

Class<ArrayList<SomeType>> listClass =
              (Class<ArrayList<SomeType>>)(Class)ArrayList.class;
ArgumentCaptor<ArrayList<SomeType>> argument = ArgumentCaptor.forClass(listClass);

This will give some warnings about unsafe casts, and of course your ArgumentCaptor can't really differentiate between ArrayList<SomeType> and ArrayList<AnotherType> without maybe inspecting the elements.

(As mentioned in the other answer, while this is a general generics problem, there is a Mockito-specific solution for the type-safety problem with the @Captor annotation. It still can't distinguish between an ArrayList<SomeType> and an ArrayList<OtherType>.)

Edit:

Take also a look at tenshis comment. You can change the original code from Paulo Ebermann to this (much simpler)

final ArgumentCaptor<List<SomeType>> listCaptor
        = ArgumentCaptor.forClass((Class) List.class);

Based on @tenshi's and @pkalinow's comments (also kudos to @rogerdpack), the following is a simple solution for creating a list argument captor that also disables the "uses unchecked or unsafe operations" warning:

@SuppressWarnings("unchecked")
final ArgumentCaptor<List<SomeType>> someTypeListArgumentCaptor =
    ArgumentCaptor.forClass(List.class);

Full example here and corresponding passing CI build and test run here.

Our team has been using this for some time in our unit tests and this looks like the most straightforward solution for us.


I had the same issue with testing activity in my Android app. I used ActivityInstrumentationTestCase2 and MockitoAnnotations.initMocks(this); didn't work. I solved this issue with another class with respectively field. For example:

class CaptorHolder {

        @Captor
        ArgumentCaptor<Callback<AuthResponse>> captor;

        public CaptorHolder() {
            MockitoAnnotations.initMocks(this);
        }
    }

Then, in activity test method:

HubstaffService hubstaffService = mock(HubstaffService.class);
fragment.setHubstaffService(hubstaffService);

CaptorHolder captorHolder = new CaptorHolder();
ArgumentCaptor<Callback<AuthResponse>> captor = captorHolder.captor;

onView(withId(R.id.signInBtn))
        .perform(click());

verify(hubstaffService).authorize(anyString(), anyString(), captor.capture());
Callback<AuthResponse> callback = captor.getValue();

There is an open issue in Mockito's GitHub about this exact problem.

I have found a simple workaround that does not force you to use annotations in your tests:

import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.MockitoAnnotations;

public final class MockitoCaptorExtensions {

    public static <T> ArgumentCaptor<T> captorFor(final CaptorTypeReference<T> argumentTypeReference) {
        return new CaptorContainer<T>().captor;
    }

    public static <T> ArgumentCaptor<T> captorFor(final Class<T> argumentClass) {
        return ArgumentCaptor.forClass(argumentClass);
    }

    public interface CaptorTypeReference<T> {

        static <T> CaptorTypeReference<T> genericType() {
            return new CaptorTypeReference<T>() {
            };
        }

        default T nullOfGenericType() {
            return null;
        }

    }

    private static final class CaptorContainer<T> {

        @Captor
        private ArgumentCaptor<T> captor;

        private CaptorContainer() {
            MockitoAnnotations.initMocks(this);
        }

    }

}

What happens here is that we create a new class with the @Captor annotation and inject the captor into it. Then we just extract the captor and return it from our static method.

In your test you can use it like so:

ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(genericType());

Or with syntax that resembles Jackson's TypeReference:

ArgumentCaptor<Supplier<Set<List<Object>>>> fancyCaptor = captorFor(
    new CaptorTypeReference<Supplier<Set<List<Object>>>>() {
    }
);

It works, because Mockito doesn't actually need any type information (unlike serializers, for example).


List<String> mockedList = mock(List.class);

List<String> l = new ArrayList();
l.add("someElement");

mockedList.addAll(l);

ArgumentCaptor<List> argumentCaptor = ArgumentCaptor.forClass(List.class);

verify(mockedList).addAll(argumentCaptor.capture());

List<String> capturedArgument = argumentCaptor.<List<String>>getValue();

assertThat(capturedArgument, hasItem("someElement"));

Examples related to java

Under what circumstances can I call findViewById with an Options Menu / Action Bar item? How much should a function trust another function How to implement a simple scenario the OO way Two constructors How do I get some variable from another class in Java? this in equals method How to split a string in two and store it in a field How to do perspective fixing? String index out of range: 4 My eclipse won't open, i download the bundle pack it keeps saying error log

Examples related to unit-testing

Deprecated Gradle features were used in this build, making it incompatible with Gradle 5.0 How to test the type of a thrown exception in Jest Unit Tests not discovered in Visual Studio 2017 Class Not Found: Empty Test Suite in IntelliJ Angular 2 Unit Tests: Cannot find name 'describe' Enzyme - How to access and set <input> value? Mocking HttpClient in unit tests Example of Mockito's argumentCaptor How to write unit testing for Angular / TypeScript for private methods with Jasmine Why is the Visual Studio 2015/2017/2019 Test Runner not discovering my xUnit v2 tests

Examples related to junit

Eclipse No tests found using JUnit 5 caused by NoClassDefFoundError for LauncherFactory How to resolve Unneccessary Stubbing exception JUnit 5: How to assert an exception is thrown? How do I mock a REST template exchange? Class Not Found: Empty Test Suite in IntelliJ Unable to find a @SpringBootConfiguration when doing a JpaTest Failed to load ApplicationContext (with annotation) Example of Mockito's argumentCaptor Mockito - NullpointerException when stubbing Method Spring jUnit Testing properties file

Examples related to mockito

How to resolve Unneccessary Stubbing exception How do I mock a REST template exchange? Mockito : doAnswer Vs thenReturn Example of Mockito's argumentCaptor Mockito: Mock private field initialization Mockito - NullpointerException when stubbing Method Usages of doThrow() doAnswer() doNothing() and doReturn() in mockito Java verify void method calls n times with Mockito Unfinished Stubbing Detected in Mockito How do I mock an autowired @Value field in Spring with Mockito?