Skip to content

AdditionalMatchers.and() and or() swap matcher order #3331

@DidierLoiseau

Description

@DidierLoiseau

Consider the following code:

@ExtendWith(MockitoExtension.class)
public class MockitoTest {
  @Mock MyService myService;

  @Test
  public void test() {
    Mockito.when(
            myService.doSomething(
                and(any(MyPojo.class),
                    argThat(p -> p.id == 1))))
        .thenReturn(1);

    myService.doSomething("hello");
  }

  interface MyService {
    int doSomething(Object pojo);
  }

  static class MyPojo {
    int id = 1;
  }
}

This test throws the following exception:

java.lang.ClassCastException: class java.lang.String cannot be cast to class MockitoTest$MyPojo (java.lang.String is in module java.base of loader 'bootstrap'; MockitoTest$MyPojo is in unnamed module of loader 'app')
	at org.mockito.internal.matchers.And.matches(And.java:23)
	at org.mockito.internal.invocation.TypeSafeMatching.apply(TypeSafeMatching.java:35)
	at org.mockito.internal.invocation.MatcherApplicationStrategy.argsMatch(MatcherApplicationStrategy.java:85)
	at org.mockito.internal.invocation.MatcherApplicationStrategy.forEachMatcherAndArgument(MatcherApplicationStrategy.java:71)
	at org.mockito.internal.invocation.InvocationMatcher.argumentsMatch(InvocationMatcher.java:156)
	at org.mockito.internal.invocation.InvocationMatcher.matches(InvocationMatcher.java:82)
	at org.mockito.internal.stubbing.InvocationContainerImpl.findAnswerFor(InvocationContainerImpl.java:92)
	at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:91)
	at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:29)
	at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:34)
	at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:82)
	at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:56)
	at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor$DispatcherDefaultingToRealMethod.interceptAbstract(MockMethodInterceptor.java:161)
	at MockitoTest$MyService$MockitoMock$v3DeXXsQ.doSomething(Unknown Source)
	at MockitoTest.test(MockitoTest.java:23)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)

Debugger inspection of And.matches() shows that m1 is the lambda and m2 is the InstanceOf, as opposed to what was passed to and(). I guess this comes from ArgumentMatcherStorageImpl#reportAnd() and reportOr() which pop m1 before m2, swapping the order.

This is with Mockito 5.11.0, Junit 5.10.2 and Java 11.

check that

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions