ChatGPT解决这个技术问题 Extra ChatGPT

How to use ArgumentCaptor for stubbing?

In Mockito documentation and javadocs it says

It is recommended to use ArgumentCaptor with verification but not with stubbing.

but I don't understand how ArgumentCaptor can be used for stubbing. Can someone explain the above statement and show how ArgumentCaptor can be used for stubbing or provide a link that shows how it can be done?

Super short & nice explanation here : dzone.com/articles/…

D
David Rawson

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));

Thanks for the answer. I have a question. In the third code block we know that true is returned only when expected is passed to doSomething. But when is true returned in the second code block? Or does someObject always return true for someMethod in that case?
Hm, I believe you meant "But when is true returned in the third code block?". In third code block we just don't care for return value and let it be default one. For boolean it is false, not true.
No I counted all grey background blocks as code blocks. Including the first one liner. I was referring to the line when(someObject.doSomething(argumentCaptor.capture())).thenReturn(true);
Ah, sorry. Yes, in this case true will be returned always.
not sure the reason to "not use with stubbing" is a simple reason. matchers don't give us the actual expected argument (just the type) and leads to being okay with tests passing despite arguments that might be wrong.
A
Aubergine

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.


S
Stefan Mondelaers

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.


Answering your question, argumentCaptor.capture() does capture the value that is passed to the stubbed method, this is useful for when you have something like: "someObject.doSomething(new OtherObject(4))" in that case the captor will get that OtherObject instance that you can then use to verify that a 4 was passed
@raspacorp argumentCaptor.capture() does capture the value that is passed to the stubbed method when used in a vertification method. It doesn't when used while stubbing (when method) because there is nothing to be captured at that time (the logic to be tested is not called yet).