mockito-java8
mockito-java8 copied to clipboard
Lambda in assertArg called twice
We have found an interesting issue, that seems to be caused by assertArg. For the following test
verify(mockedService).functionThatExpectsAnInputStream(assertArg((InputStream is) -> {
try {
// Oh my god this thing gets invoked twice !!
assertThat(IOUtils.toByteArray(is),is("a,b,c".getBytes()));
} catch (IOException e) {
fail();
}
}));
The lambda block gets invoked twice, being the IS in the second execution empty.
However if the same logic is done using a regular ArgumentCaptor, it works as expected (a single execution is done):
ArgumentCaptor<InputStream> is = ArgumentCaptor.forClass(InputStream.class);
verify(mockedService).functionThatExpectsAnInputStream(is.capture());
assertThat(is.getValue(),notNullValue());
assertThat(IOUtils.toByteArray(is.getValue()),is("a,b,c".getBytes()));
We could provide further context if needed.
Thank you very much.
Thanks for your report and sorry for delay.
It looks like that DispatcherDefaultingToRealMethod.interceptSuperCallable()
is called twice which results in double execution of passed lambda and I don't know how much I can do about that. In case of "classic" verify construction this method (from Byte Buddy creation package) is also called twice, but it is later cut off at some level and in the end captor.capture()
is not called twice.
@raphw is it normal/required to have DispatcherDefaultingToRealMethod.interceptSuperCallable()
called twice? Maybe you have any good suggestion what could be done with this issue?
I forgot. The issue is reproduced in a (commented out) test in a separate branch: https://github.com/szpak/mockito-java8/blob/issue/5-assertArgCalledTwice/src/test/java/info/solidsoft/mockito/java8/AssertionMatcherTest.java#L84-L94
Hi, I think this is an implementation detail of Mockito and has nothing to do with Byte Buddy. The problem occures in the Times
class:
public void verify(VerificationData data) {
if(this.wantedCount > 0) {
MissingInvocationChecker numberOfInvocations = new MissingInvocationChecker();
numberOfInvocations.check(data.getAllInvocations(), data.getWanted()); // 1
}
NumberOfInvocationsChecker numberOfInvocations1 = new NumberOfInvocationsChecker();
numberOfInvocations1.check(data.getAllInvocations(), data.getWanted(), this.wantedCount); // 2
}
The matcher is triggered once by (1) and once by (2). I assume that the original implementation did not consider that the matchers would trigger mutation.
The easiest solution would be to fix this in Mockito, to be honest. A quicker fix would be to wrap the matchers to cache the result of the previous invocation.