How to use ArgumentCaptor for stubbing?

Solution 1:

Assuming the following method to test:

public boolean doSomething(SomeClass arg);

Mockito documentation says that you should not use captor in this way:

when(someObject.doSomething(argumentCaptor.capture())).thenReturn(true);
assertThat(argumentCaptor.getValue(), equalTo(expected));

Because you can just use matcher during stubbing:

when(someObject.doSomething(eq(expected))).thenReturn(true);

But verification is a different story. If your test needs to ensure that this method was called with a specific argument, use ArgumentCaptor and this is the case for which it is designed:

ArgumentCaptor<SomeClass> argumentCaptor = ArgumentCaptor.forClass(SomeClass.class);
verify(someObject).doSomething(argumentCaptor.capture());
assertThat(argumentCaptor.getValue(), equalTo(expected));

Solution 2:

Hypothetically, if search landed you on this question then you probably want this:

doReturn(someReturn).when(someObject).doSomething(argThat(argument -> argument.getName().equals("Bob")));

Why? Because like me you value time and you are not going to implement .equals just for the sake of the single test scenario.

And 99 % of tests fall apart with null returned from Mock and in a reasonable design you would avoid return null at all costs, use Optional or move to Kotlin. This implies that verify does not need to be used that often and ArgumentCaptors are just too tedious to write.

Solution 3:

The line

when(someObject.doSomething(argumentCaptor.capture())).thenReturn(true);

would do the same as

when(someObject.doSomething(Matchers.any())).thenReturn(true);

So, using argumentCaptor.capture() when stubbing has no added value. Using Matchers.any() shows better what really happens and therefor is better for readability. With argumentCaptor.capture(), you can't read what arguments are really matched. And instead of using any(), you can use more specific matchers when you have more information (class of the expected argument), to improve your test.

And another problem: If using argumentCaptor.capture() when stubbing it becomes unclear how many values you should expect to be captured after verification. We want to capture a value during verification, not during stubbing because at that point there is no value to capture yet. So what does the argument captors capture method capture during stubbing? It capture anything because there is nothing to be captured yet. I consider it to be undefined behavior and I don't want to use undefined behavior.