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

Mocktio 5.0.x and later on Java 11/17 fails when mocking interface default methods #3013

Closed
jxblum opened this issue May 16, 2023 · 7 comments

Comments

@jxblum
Copy link

jxblum commented May 16, 2023

I am running Java 17 with JUnit 5.9.3 and Mockito 5.3.1 on Mac OS 13.3.1.

Java Version

$ java -version
java version "17.0.7" 2023-04-18 LTS
Java(TM) SE Runtime Environment (build 17.0.7+8-LTS-224)
Java HotSpot(TM) 64-Bit Server VM (build 17.0.7+8-LTS-224, mixed mode, sharing)

Maven Dependencies

$ mvn dependency:tree
[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------------< org.codeprimate:cp-labs >-----------------------
[INFO] Building Codeprimate Labs 1.0.0-SNAPSHOT
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- dependency:2.8:tree (default-cli) @ cp-labs ---
[WARNING] Parameter 'localRepository' is deprecated core expression; Avoid use of ArtifactRepository type. If you need access to local repository, switch to '${repositorySystemSession}' expression and get LRM from it instead.
[INFO] org.codeprimate:cp-labs:jar:1.0.0-SNAPSHOT
[INFO] +- org.codeprimate:cp-elements:jar:1.0.2:compile
[INFO] +- org.junit.jupiter:junit-jupiter:jar:5.9.3:test
[INFO] |  +- org.junit.jupiter:junit-jupiter-api:jar:5.9.3:test
[INFO] |  |  +- org.opentest4j:opentest4j:jar:1.2.0:test
[INFO] |  |  +- org.junit.platform:junit-platform-commons:jar:1.9.3:test
[INFO] |  |  \- org.apiguardian:apiguardian-api:jar:1.1.2:test
[INFO] |  +- org.junit.jupiter:junit-jupiter-params:jar:5.9.3:test
[INFO] |  \- org.junit.jupiter:junit-jupiter-engine:jar:5.9.3:test
[INFO] |     \- org.junit.platform:junit-platform-engine:jar:1.9.3:test
[INFO] +- org.assertj:assertj-core:jar:3.24.2:test
[INFO] |  \- net.bytebuddy:byte-buddy:jar:1.12.21:test
[INFO] +- junit:junit:jar:4.13.2:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] +- org.projectlombok:lombok:jar:1.18.24:test
[INFO] +- org.mockito:mockito-core:jar:5.3.1:test
[INFO] |  +- net.bytebuddy:byte-buddy-agent:jar:1.14.4:test
[INFO] |  \- org.objenesis:objenesis:jar:3.3:test
[INFO] \- edu.umd.cs.mtc:multithreadedtc:jar:1.01:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  1.297 s
[INFO] Finished at: 2023-05-15T21:36:27-07:00
[INFO] ------------------------------------------------------------------------

The following example:

https://gist.github.com/jxblum/570a58ccf0e4590c3a97b82901c95952

Does not work correct with Mockito 5.0.0 or later, up to and including 5.3.1.

This used to work on Mockito 4.11.0 (pre 5.0) but no longer works!

The following error occurs:

[INFO] Running examples.MockingInterfaceDefaultMethodsUnitTests
Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.934 s <<< FAILURE! - in examples.MockingInterfaceDefaultMethodsUnitTests
[ERROR] examples.MockingInterfaceDefaultMethodsUnitTests.containsAllKeysReturnsTrue  Time elapsed: 0.918 s  <<< FAILURE!
org.opentest4j.AssertionFailedError: 

Expecting value to be true but was false
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
	at examples.MockingInterfaceDefaultMethodsUnitTests.containsAllKeysReturnsTrue(MockingInterfaceDefaultMethodsUnitTests.java:52)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727)
	at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86)
	at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103)
	at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92)
	at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:147)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:127)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:90)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:55)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:102)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.apache.maven.surefire.junitplatform.LazyLauncher.execute(LazyLauncher.java:50)
	at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.execute(JUnitPlatformProvider.java:184)
	at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invokeAllTests(JUnitPlatformProvider.java:148)
	at org.apache.maven.surefire.junitplatform.JUnitPlatformProvider.invoke(JUnitPlatformProvider.java:122)
	at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:385)
	at org.apache.maven.surefire.booter.ForkedBooter.execute(ForkedBooter.java:162)
	at org.apache.maven.surefire.booter.ForkedBooter.run(ForkedBooter.java:507)
	at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:495)

[INFO] 
[INFO] Results:
[INFO] 
[ERROR] Failures: 
[ERROR]   MockingInterfaceDefaultMethodsUnitTests.containsAllKeysReturnsTrue:52 
Expecting value to be true but was false
[INFO] 
[ERROR] Tests run: 1, Failures: 1, Errors: 0, Skipped: 0
[INFO] 
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  4.587 s
[INFO] Finished at: 2023-05-15T21:38:08-07:00
[INFO] ------------------------------------------------------------------------

This is even a problem on Java 11, though I suspect the Java version has little to do with the underlying issue.

@jxblum jxblum changed the title Mocktio 5.0.x and later on Java 17 fails when mocking interface default methods Mocktio 5.0.x and later on Java 11/17 fails when mocking interface default methods May 16, 2023
@jxblum
Copy link
Author

jxblum commented May 16, 2023

Same problem on OpenJDK 17:

$ java -version
openjdk version "17.0.7" 2023-04-18
OpenJDK Runtime Environment Temurin-17.0.7+7 (build 17.0.7+7)
OpenJDK 64-Bit Server VM Temurin-17.0.7+7 (build 17.0.7+7, mixed mode, sharing)
jblumWLVDQ:cp-labs jblum$ 

Again, I don't think Java version matters from 11 and beyond.

@jxblum
Copy link
Author

jxblum commented May 16, 2023

It doesn't even matter if the boolean contains(KEY key) method is not a default method, as in:

  interface Cache<KEY extends Comparable<KEY>, VALUE> extends Iterable<KEY> {

    boolean contains(KEY key);

    @SuppressWarnings("unchecked")
    default boolean containsAll(KEY... keys) {
      return Arrays.stream(keys).allMatch(this::contains);
    }
  }

@jxblum
Copy link
Author

jxblum commented May 16, 2023

Given a custom List:

  interface ExtList<T> extends List<T> {

    @SuppressWarnings("unchecked")
    default boolean containsAll(T... elements) {
      return Arrays.stream(elements).allMatch(this::contains);
    }
  }

This test case passes:

  @Test
  @SuppressWarnings("unchecked")
  public void listContainsAllElementsReturnsTrue() {

    ExtList<Object> mockList = mock(ExtList.class);

    doReturn(true).when(mockList).contains(any());
    doCallRealMethod().when(mockList).containsAll(any(Object[].class));

    assertThat(mockList.containsAll("ONE", "TWO")).isTrue();

    verify(mockList, times(1)).containsAll(eq("ONE"), eq("TWO"));

    Arrays.asList("ONE", "TWO").forEach(element ->
      verify(mockList, times(1)).contains(eq(element)));

    verifyNoMoreInteractions(mockList);
  }

Though, I am not sure why the Cache interface definition and List definition vary such that Mockito fails with the former but passes with the later.

Update Gist: https://gist.github.com/jxblum/570a58ccf0e4590c3a97b82901c95952

@jxblum
Copy link
Author

jxblum commented May 16, 2023

After a bit more experimentation, the test only passes when the arrangement on the mock is the following:

    doCallRealMethod().when(mockCache).containsAll(any(), any());

Given 2 arguments.

How to mock a vararg method in Mockito 5?

@TimvdLippe
Copy link
Contributor

Stubbing varargs methods has changed in Mockito 5 and is documented in the release notes: https://github.com/mockito/mockito/releases/tag/v5.0.0

@jxblum
Copy link
Author

jxblum commented May 16, 2023

According to the Javadoc for ArgumentMatchers.any(), this should match varargs (in addition to null),so this should be correct:

doCallRealMethod().when(mockCache).containsAll(any());

@TimvdLippe
Copy link
Contributor

The JavaDoc has been updated in #3003

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants