mockito-java8 icon indicating copy to clipboard operation
mockito-java8 copied to clipboard

Lambda in assertArg called twice

Open CalamarBicefalo opened this issue 9 years ago • 3 comments

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.

CalamarBicefalo avatar Jan 06 '16 15:01 CalamarBicefalo

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?

szpak avatar Mar 27 '16 19:03 szpak

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

szpak avatar Mar 27 '16 19:03 szpak

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.

raphw avatar Apr 02 '16 01:04 raphw