Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ArgumentCaptor A single capture() call captures all vararg elements. #585

Closed
ChristianSchwarz opened this issue Aug 23, 2016 · 7 comments · Fixed by #2835
Closed

ArgumentCaptor A single capture() call captures all vararg elements. #585

ChristianSchwarz opened this issue Aug 23, 2016 · 7 comments · Fixed by #2835

Comments

@ChristianSchwarz
Copy link
Contributor

A single ArgumentCaptor.capture() call captures all vararg elements.

mock.varargs("1","2","3");

// captures "1","2","3"
verify(mock).varargs(c.capture()); 

// fails with arguments are different
verify(mock).varargs(c.capture(),
                               c.capture()); 


// captures "1","2","3"
verify(mock).varargs(c.capture(),
                               c.capture(),
                               c.capture()); 

Expected: One capture() call should only capture one vararg argument
Actual: A single ArgumentCaptor.capture() call captures all vararg elements.

@mockitoguy
Copy link
Member

mockitoguy commented Aug 23, 2016

Respectful -1. The current actual behavior is more intuitive and looks better in code IMHO.

@bric3 bric3 added this to the 2.1 milestone Aug 23, 2016
@bric3
Copy link
Contributor

bric3 commented Aug 23, 2016

I agree with @szczepiq, the behaviour makes sense today, that's what I would expect from capture

However I think ArgumentCaptor could benefit from a more explicit API, for hardened tests on vararg. I think it should happen in 2.1.

I'm not sure we will change the behavior or introduce a new API, we will see.

@ChristianSchwarz
Copy link
Contributor Author

The problem of "a single captor captures all varags" is that you can not ensure that only one argument is passed. Here an example:

mock.vararg("1","2"); 
verify(mock).vararg(captor.capture()); 
//^ should fail cause we want to verify that only one 
//  argument was passed and we want capture it

@mockitoguy
Copy link
Member

I don't get the example. I can add assertions on captured values, can I not?

@ChristianSchwarz
Copy link
Contributor Author

I can add assertions on captured values

Sure, but you can't use the capturing matcher to verify the number of arguments passed to the varargs method like e.g. isNotNull() does.

In the following scenario you can't ensure that the method was always called with exactly 2 arguments:

mock.vararg("1","2","3"); 
mock.vararg("4","5"); 
mock.vararg("6"); 
verify(mock, times(3)).vararg(captor.capture()); 

Maybe this works, haven't tested:

mock.vararg("1","2","3"); 
mock.vararg("4","5"); 
mock.vararg("6"); 
verify(mock, times(3)).vararg(or(anyString(),captor.capture()),
                              or(anyString(),captor.capture()))); 

@mockitoguy
Copy link
Member

Hmmm, I take your point that this case is not possible to test ATM. Is it a real use case from one of your tests? It seems like an ultra corner case.

@big-andy-coates
Copy link
Contributor

big-andy-coates commented Dec 29, 2022

With #2835 this now works:

ArgumentCaptor<String[]> captor = ArgumentCaptor.forClass(String[].class);
mock.vararg("1","2","3"); 
mock.vararg("4","5"); 
mock.vararg("6"); 
verify(mock, times(3)).vararg(captor.capture()); 

// captor.getAllValues() will return List.of(new String[]{"1","2","3"}, new String[]{"4","5"}, new String[]{"6"})

It is also possible to verify only a single value was passed:

ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
mock.vararg("1","2","3"); 
mock.vararg("4","5"); 
mock.vararg("6"); 
verify(mock, times(1)).vararg(captor.capture());  // <-- only last call matches.

TimvdLippe pushed a commit that referenced this issue Dec 30, 2022
BREAKING CHANGE: This changes the default behaviour of the `any()` matcher, argument captors and `MockitoHamcrest` matchers when passed to a varargs parameter.  Previously, these matcher would match each element in the varargs parameter, matching any number of elements 0...n. From Mockito v5 onwards, when passed to a varargs parameter, will match invocations where a single value is passed to the varargs parameter. To match any number of values passed to the varargs parameter, pass the type of the varargs parameter to the matcher. For example, given a `String...` varargs parameter, use `any(String[].class)`.

Fixes #2836
Fixes #1593
Fixes #585
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
No open projects
Development

Successfully merging a pull request may close this issue.

5 participants