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

Mock enum with methods fails on Java 17 #2315

Closed
johanjanssen-sanoma opened this issue Jun 4, 2021 · 29 comments
Closed

Mock enum with methods fails on Java 17 #2315

johanjanssen-sanoma opened this issue Jun 4, 2021 · 29 comments
Assignees

Comments

@johanjanssen-sanoma
Copy link

The following fails on Java 17 and works on 11 and 16:

public enum ExampleEnum {
    TEST {
        public String retrieve() {
            return "test";
        }
    };

    public String getValue() {
        return "21";
    }
}
public class Container {
    private ExampleEnum exampleEnum;
    public Container(ExampleEnum exampleEnum) {
        this.exampleEnum = exampleEnum;
    }

    public String retrieveValue() {
        return exampleEnum.getValue();
    }
}
@ExtendWith(MockitoExtension.class)
public class ExampleEnumTest {

    @Mock
    private ExampleEnum exampleEnum;

    @Test
    public void testEnumWithMethods() {
        Container container = new Container(exampleEnum);
        Mockito.when(exampleEnum.getValue()).thenReturn("42");
        assertEquals("42", container.retrieveValue());
    }
}

On 17 it gives the following error:

org.mockito.exceptions.base.MockitoException:

Mockito cannot mock this class: class com.example.ExampleEnum.

If you're not sure why you're getting this error, please report to the mailing list.


Java               : 17
JVM vendor name    : Oracle Corporation
JVM vendor version : 17-ea+24-2164
JVM name           : OpenJDK 64-Bit Server VM
JVM version        : 17-ea+24-2164
JVM info           : mixed mode, sharing
OS name            : Linux
OS version         : 4.19.76-linuxkit


You are seeing this disclaimer because Mockito is configured to create inlined mocks.
You can learn about inline mocks and their limitations under item #39 of the Mockito class javadoc.

Underlying exception : org.mockito.exceptions.base.MockitoException: Could not modify all classes [interface java.lang.constant.Constable, class java.lang.Object, interface java.lang.Comparable, interface java.io.Serializable, class java.lang.Enum, class com.example.ExampleEnum]
Caused by: org.mockito.exceptions.base.MockitoException: Could not modify all classes [interface java.lang.constant.Constable, class java.lang.Object, interface java.lang.Comparable, interface java.io.Serializable, class java.lang.Enum, class com.example.ExampleEnum]
Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the class NestHost, NestMembers, Record, or PermittedSubclasses attribute

The complete code example can be found here: https://github.com/johanjanssen/JavaUpgrades/tree/main/java17

It can be build with Docker for instance with (17 can be replaced with 11 and 16):
docker build -t javaupgrades -f ..\Dockerfile --build-arg JDK_VERSION=17 .

Is there some way I can work around this issue?

@TimvdLippe
Copy link
Contributor

This might be related to openjdk/jdk#4015

@johanjanssen-sanoma
Copy link
Author

I thought about that as well, but that one mostly results in errors like
module java.base does not "opens java.lang" to unnamed module

But maybe that error is swallowed somewhere.

@raphw
Copy link
Member

raphw commented Jun 10, 2021

When I run this on a recent JDK 17, it all works. The underlying error is: java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the class NestHost, NestMembers, Record, or PermittedSubclasses attribute. Normally, this indicates that BB changed an attribute but this would surprise me.

@johanjanssen-sanoma
Copy link
Author

Thanks for checking it, I've upgraded to the latest OpenJDK and the latest Mockito. But the error is still there:

`[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 2.287 s <<< FAILURE! - in com.example.ExampleEnumTest
[ERROR] testEnumWithMethods Time elapsed: 2.267 s <<< ERROR!
org.mockito.exceptions.base.MockitoException:

Mockito cannot mock this class: class com.example.ExampleEnum.

If you're not sure why you're getting this error, please report to the mailing list.

Java : 17
JVM vendor name : Oracle Corporation
JVM vendor version : 17-ea+25-2252
JVM name : OpenJDK 64-Bit Server VM
JVM version : 17-ea+25-2252
JVM info : mixed mode, sharing
OS name : Windows 10
OS version : 10.0

You are seeing this disclaimer because Mockito is configured to create inlined mocks.
You can learn about inline mocks and their limitations under item #39 of the Mockito class javadoc.

Underlying exception : org.mockito.exceptions.base.MockitoException: Could not modify all classes [class java.lang.Object, class java.lang.Enum, interface java.lang.Comparable, interface java.io.Serializable, class com.example.ExampleEnum, interface java.lang.constant.Constable]
Caused by: org.mockito.exceptions.base.MockitoException: Could not modify all classes [class java.lang.Object, class java.lang.Enum, interface java.lang.Comparable, interface java.io.Serializable, class com.example.ExampleEnum, interface java.lang.constant.Constable]
Caused by: java.lang.UnsupportedOperationException: class redefinition failed: attempted to change the class NestHost, NestMembers, Record, or PermittedSubclasses attribute`

How did you run the example? Did you use use my GitHub project https://github.com/johanjanssen/JavaUpgrades/tree/main/java17/mockito_broken? Or did you create your own based on the code I pasted above?

I wonder if the error then has something to do with the dependencies I'm using.

@johanjanssen-sanoma
Copy link
Author

I've just double checked by running my GitHub repo example in Docker images. It fails with maven:3.8.1-openjdk-17-slim and succeeds with maven:3.8.1-openjdk-16-slim. I really wonder what's the difference between our setups :-).

@raphw
Copy link
Member

raphw commented Jun 11, 2021

I did find the problem after all. It seems like permitted subclasses are appended twice upon a retransformation what sets of the verifier. I need to see why this happens and will fix this as soon as I find the time.

@raphw
Copy link
Member

raphw commented Jun 11, 2021

I identified the issue, it seems to be a bug or a documentation issue in ASM: https://gitlab.ow2.org/asm/asm/-/issues/317948

I will create a new version once that is resolved.

@johanjanssen-sanoma
Copy link
Author

Interesting, thanks a lot for your help!

@raphw
Copy link
Member

raphw commented Jun 12, 2021

Fixed it on master, it's a documentation error in ASM so I don't have to wait for a release. I have some other minor issues I have to address but it will be fixed eventually.

@johanjanssen
Copy link

Just tried my example on the newly released 3.11.2. Now it works! Thanks a lot @raphw for fixing it so soon!

@pburka
Copy link

pburka commented Aug 17, 2021

Mocking enums isn't working for me on Java 17, even with 3.11.2. I get a java.lang.IncompatibleClassChangeError: class ...$MockitoMock$549794057 cannot inherit from sealed class ....

@raphw
Copy link
Member

raphw commented Aug 17, 2021

Yeah, sealed types put an end to this. We should however supply better error messages: #2392

@pburka
Copy link

pburka commented Aug 17, 2021

I guessed that was probably the case. In #2392 you say "sealed classes can only be mocked if they are real inline mocks." What do you mean by "real inline mocks"? Is there some workaround to mock sealed classes?

@raphw
Copy link
Member

raphw commented Aug 17, 2021

The inline mock maker needs to create subclasses when mocking abstract types or interfaces. Otherwise the classes cannot be instantiated. If those mocks are sealed, we are out of luck.

@jbrinegar
Copy link

jbrinegar commented Aug 31, 2021

With version 3.12.4, I see exceptions that originate with:

Caused by: java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessDeclaredMembers")

...but only when a security manager is active on Java 17. If I use Java 16, or if I remove the security manager, I don't have problems with mocking enums. Most curiously, if I ask the security manager about this specific permission, I do have it. I am happy to separate this into a new issue and/or provide a reproducer -- just let me know.

@raphw
Copy link
Member

raphw commented Aug 31, 2021

Can you provide a full stack?

@jbrinegar
Copy link

@raphw I have narrowed the issue down some -- I can reproduce the problem with mockito 3.11.2, but not with 3.11.1.

Here is the full stack from 3.12.4:

SecurityManagerTest withMockito() FAILED

  org.mockito.exceptions.base.MockitoException:
  Mockito cannot mock this class: class com.justinbrinegar.SecurityManagerTest$Pet.

  If you're not sure why you're getting this error, please report to the mailing list.


  Java               : 17
  JVM vendor name    : jbrinegar-testing
  JVM vendor version : 17+0-20210830011742
  JVM name           : OpenJDK 64-Bit Server VM
  JVM version        : 17+0-20210830011742
  JVM info           : mixed mode, sharing
  OS name            : Mac OS X
  OS version         : 11.5
  You are seeing this disclaimer because Mockito is configured to create inlined mocks.
  You can learn about inline mocks and their limitations under item #39 of the Mockito class javadoc.

  Underlying exception : org.mockito.exceptions.base.MockitoException: Could not modify all classes [class com.justinbrinegar.SecurityManagerTest$Pet, class java.lang.Object]
      at app//com.justinbrinegar.SecurityManagerTest.withMockito(SecurityManagerTest.java:19)
      at java.base@17/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at java.base@17/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
      at java.base@17/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.base@17/java.lang.reflect.Method.invoke(Method.java:568)
      at app//org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
      at app//org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
      at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
      at app//org.junit.jupiter.engine.extension.TimeoutInvocation.proceed(TimeoutInvocation.java:46)
      at app//org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
      at app//org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
      at app//org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
      at app//org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
      at app//org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
      at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
      at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
      at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
      at app//org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
      at app//org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
      at app//org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
      at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
      at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
      at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
      at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
      at app//org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
      at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
      at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
      at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
      at java.base@17/java.util.ArrayList.forEach(ArrayList.java:1511)
      at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
      at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
      at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
      at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
      at java.base@17/java.util.ArrayList.forEach(ArrayList.java:1511)
      at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
      at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
      at app//org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
      at app//org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
      at app//org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
      at app//org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
      at app//org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
      at app//org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
      at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
      at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
      at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
      at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
      at app//org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
      at app//org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
      at app//org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
      at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
      at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
      at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
      at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
      at java.base@17/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at java.base@17/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
      at java.base@17/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.base@17/java.lang.reflect.Method.invoke(Method.java:568)
      at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
      at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
      at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
      at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
      at jdk.proxy2/jdk.proxy2.$Proxy5.stop(Unknown Source)
      at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
      at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
      at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
      at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
      at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
      at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:133)
      at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:71)
      at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
      at app//worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
  Caused by: org.mockito.exceptions.base.MockitoException: Could not modify all classes [class com.justinbrinegar.SecurityManagerTest$Pet, class java.lang.Object]
      at app//net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:153)
      at app//net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:366)
      at app//net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:175)
      at app//net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:377)
      ... 85 more
  Caused by: java.lang.IllegalStateException:
  Byte Buddy could not instrument all classes within the mock's type hierarchy

  This problem should never occur for javac-compiled classes. This problem has been observed for classes that are:
   - Compiled by older versions of scalac
   - Classes that are part of the Android distribution
      at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:280)
      at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.mockClass(InlineBytecodeGenerator.java:213)
      at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.lambda$mockClass$0(TypeCachingBytecodeGenerator.java:47)
      at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:153)
      at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:366)
      at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:175)
      at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:377)
      at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClass(TypeCachingBytecodeGenerator.java:40)
      at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.createMockType(InlineDelegateByteBuddyMockMaker.java:391)
      at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.doCreateMock(InlineDelegateByteBuddyMockMaker.java:351)
      at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.createMock(InlineDelegateByteBuddyMockMaker.java:330)
      at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createMock(InlineByteBuddyMockMaker.java:58)
      at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:53)
      at org.mockito.internal.MockitoCore.mock(MockitoCore.java:84)
      at org.mockito.Mockito.mock(Mockito.java:1964)
      at org.mockito.Mockito.mock(Mockito.java:1879)
      ... 85 more
  Caused by: java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "accessDeclaredMembers")
      at java.base/java.security.AccessControlContext.checkPermission(AccessControlContext.java:485)
      at java.base/java.security.AccessController.checkPermission(AccessController.java:1068)
      at java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:416)
      at java.base/java.lang.Class.checkMemberAccess(Class.java:3051)
      at java.base/java.lang.Class.getRecordComponents(Class.java:2431)
      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 net.bytebuddy.utility.Invoker$Dispatcher.invoke(Unknown Source)
      at net.bytebuddy.utility.dispatcher.JavaDispatcher$Dispatcher$ForNonStaticMethod.invoke(JavaDispatcher.java:974)
      at net.bytebuddy.utility.dispatcher.JavaDispatcher$ProxiedInvocationHandler.invoke(JavaDispatcher.java:1098)
      at net.bytebuddy.description.type.$Proxy42.getRecordComponents(Unknown Source)
      at net.bytebuddy.description.type.TypeDescription$ForLoadedType.getRecordComponents(TypeDescription.java:8795)
      at net.bytebuddy.dynamic.scaffold.InstrumentedType$Factory$Default$1.represent(InstrumentedType.java:422)
      at net.bytebuddy.ByteBuddy.redefine(ByteBuddy.java:782)
      at net.bytebuddy.ByteBuddy.redefine(ByteBuddy.java:757)
      at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.transform(InlineBytecodeGenerator.java:381)
      at java.instrument/java.lang.instrument.ClassFileTransformer.transform(ClassFileTransformer.java:244)
      at java.instrument/sun.instrument.TransformerManager.transform(TransformerManager.java:188)
      at java.instrument/sun.instrument.InstrumentationImpl.transform(InstrumentationImpl.java:541)
      at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
      at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:169)
      at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:276)
      ... 100 more

Test is:

package com.justinbrinegar;

import org.junit.jupiter.api.Test;

import static org.mockito.Mockito.mock;

class SecurityManagerTest {
    @Test
    void testSecurityManager() {
        var sm = System.getSecurityManager();
        var perm = new RuntimePermission("accessDeclaredMembers");
        if(sm != null) {
            sm.checkPermission(perm);
        }
    }

    @Test
    void withMockito() {
        var myPet = mock(Pet.class);
    }

    enum Animal {
        CAT,
        DOG
    }

    class Pet {
        String name;
        Animal animal;
        Pet(String name, Animal animal) {
            this.name = name;
            this.animal = animal;
        }
    }
}

@raphw
Copy link
Member

raphw commented Sep 1, 2021

I see, I still struggle to get the security manager under control after I had to create some custom abstractions in order to address the cut down that was introduced in Java 17. I have just pushed a new appraoch to Byte Buddy that hopefully avoids the issue altogether.

Could you checkout Byte Buddy and build it from master and try with the patched version?

@jbrinegar
Copy link

Nice! I can confirm that when using bytebuddy version 1.11.14 my test fails, but 1.11.15-SNAPSHOT causes it to succeed 🥇

@Lewox
Copy link

Lewox commented Sep 24, 2021

Hello guys, I can confirm that I still get a fairly similar issue with Java 17, Mockito 3.12.4 and Byte Buddy 1.11.18

Test

package net.lewox.mockito;

import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

class MockEnum {
	
	@Test
	void testMockEnum() {
		Mockito.mock(Animal.class);
	}
	
	enum Animal {
		CAT {
			@Override
			public String sound() {
				return "meow";
			}
		};
		
		public abstract String sound();
	}
}

Stacktrace

org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: class net.lewox.mockito.MockEnum$Animal.

If you're not sure why you're getting this error, please report to the mailing list.


Java               : 17
JVM vendor name    : Oracle Corporation
JVM vendor version : 17+35-2724
JVM name           : OpenJDK 64-Bit Server VM
JVM version        : 17+35-2724
JVM info           : mixed mode, sharing
OS name            : Windows 10
OS version         : 10.0


You are seeing this disclaimer because Mockito is configured to create inlined mocks.
You can learn about inline mocks and their limitations under item #39 of the Mockito class javadoc.

Underlying exception : org.mockito.exceptions.base.MockitoException: Unsupported settings with this type 'net.lewox.mockito.MockEnum$Animal'
	at net.lewox.mockito.MockEnum.testMockEnum(MockEnum.java:10)
	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:725)
	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:149)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
	at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
	at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	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.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:214)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:210)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
	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:107)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:95)
	at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:91)
	at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:60)
	at org.eclipse.jdt.internal.junit5.runner.JUnit5TestReference.run(JUnit5TestReference.java:98)
	at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:40)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:529)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:756)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:452)
	at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:210)
Caused by: org.mockito.exceptions.base.MockitoException: Unsupported settings with this type 'net.lewox.mockito.MockEnum$Animal'
	at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:153)
	at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:366)
	at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:175)
	at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:377)
	... 70 more

@raphw
Copy link
Member

raphw commented Sep 24, 2021

I added this as a test case to Mockito and it seems to work just fine: https://github.com/mockito/mockito/blob/main/subprojects/inline/src/test/java/org/mockitoinline/EnumMockingTest.java

Are you using the latest version and inline mock maker?

@Lewox
Copy link

Lewox commented Sep 24, 2021

Hi @raphw I'm using the versions I described above (Java 17, Mockito 3.12.4 and Byte Buddy 1.11.18), I tested both with the ByteBuddy version currently used and version 1.11.18, and I'm depdencency mockito-inline as well (same version as mockito).

openjdk version "17" 2021-09-14
OpenJDK Runtime Environment (build 17+35-2724)
OpenJDK 64-Bit Server VM (build 17+35-2724, mixed mode, sharing)

and Windows 10

On a side note, I cannot build clean version 3 tests fail, using Gradle wrapper.

@raphw
Copy link
Member

raphw commented Sep 24, 2021

Strange. Our CI disagrees: https://github.com/mockito/mockito/runs/3699826916?check_suite_focus=true

Not sure why there would be a difference on Windows. Could you download the exact JVM version that we are using and try again?

@TimvdLippe
Copy link
Contributor

Closing this per the above comment. If you can still reproduce this issue, please submit a regression test to our test suite and we can debug further, thanks!

@jonatanloya
Copy link

Mocking enums isn't working for me on Java 17, even with 3.11.2. I get a java.lang.IncompatibleClassChangeError: class ...$MockitoMock$549794057 cannot inherit from sealed class ....

@pburka Did you find a workaround? I got same issue

@pburka
Copy link

pburka commented Nov 8, 2022 via email

@namdiemefo
Copy link

@pburka could you kindly give an example of adding the interface to mock the enum (set of singletons in my use case as well).

@pburka
Copy link

pburka commented Nov 13, 2022 via email

@voghDev
Copy link

voghDev commented Nov 6, 2023

I was having the same issue, and as others have said, I can confirm that the only way I could solve the issue is not mocking sealed classes anymore. My scenario looked like the following

sealed class A {
  object B: A()
  object C: A()
}

I had to remove all references to mock<A>() and substitute them with instances of B or C

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

10 participants