5

Today I created a small test project for java 16 preview. I created a sealed interface and a final implementation.

I want to know if there is anyway to be able to mock the sealed interface so that I can test implementations that use the sealed interface.

package gluestick;

public sealed interface Test permits TestImpl {
    int doIt();
}
package gluestick;

public final class TestImpl implements Test {
    @Override
    public int doIt() {
        return 0;
    }
}
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

public class MyTest {

    @Test
    public void test() {
        Mockito.mock(gluestick.Test.class);
    }
}

This all amounts however to creating a java.lang.IncompatibleClassChangeError since the sealed class cannot be implemented.

org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: interface gluestick.Test.

Mockito can only mock non-private & non-final classes.
If you're not sure why you're getting this error, please report to the mailing list.


Java               : 16
JVM vendor name    : Oracle Corporation
JVM vendor version : 16.0.1+9-24
JVM name           : OpenJDK 64-Bit Server VM
JVM version        : 16.0.1+9-24
JVM info           : mixed mode, sharing
OS name            : Windows 10
OS version         : 10.0


Underlying exception : java.lang.IllegalStateException: Error invoking (accessor)::defineClass

    at MyTest.test(MyTest.java:8)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:688)
    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$6(TestMethodTestDescriptor.java:210)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
    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:96)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:221)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.IllegalStateException: Error invoking (accessor)::defineClass
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$UsingUnsafeInjection.defineClass(ClassInjector.java:1002)
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection.injectRaw(ClassInjector.java:254)
    at net.bytebuddy.dynamic.loading.ClassInjector$AbstractBase.inject(ClassInjector.java:113)
    at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default$InjectionDispatcher.load(ClassLoadingStrategy.java:233)
    at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:100)
    at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6147)
    at org.mockito.internal.creation.bytebuddy.SubclassBytecodeGenerator.mockClass(SubclassBytecodeGenerator.java:268)
    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.SubclassByteBuddyMockMaker.createMockType(SubclassByteBuddyMockMaker.java:77)
    at org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.createMock(SubclassByteBuddyMockMaker.java:43)
    at org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker.createMock(ByteBuddyMockMaker.java:43)
    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:1954)
    at org.mockito.Mockito.mock(Mockito.java:1865)
    ... 66 more
Caused by: java.lang.IncompatibleClassChangeError: class gluestick.Test$MockitoMock$1535486471 cannot implement sealed interface gluestick.Test
    at java.base/java.lang.ClassLoader.defineClass1(Native Method)
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1010)
    at java.base/java.lang.ClassLoader$ByteBuddyAccessor$teEoQ54g.defineClass(Unknown Source)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$UsingUnsafeInjection.defineClass(ClassInjector.java:998)
    ... 85 more


Process finished with exit code -1

Is there any way to mock a sealed interface?

Curious
  • 51
  • 1
  • 6
  • I assume you could add a MockImpl to the permits list – Stultuske Jul 19 '21 at 06:19
  • 1
    From what I know about sealed classes so far, your mocks would also have to be part of the same module and, as Stultuske said, they probably need to be part of the permits list. This might not be practical and I assume new approaches of dealing with sealed classes will emerge, e.g. sealing them after they've been tested, e.g. in some production build. – Thomas Jul 19 '21 at 06:25
  • @Stultuske I thought about this, and thought it could be viable, but then I started to think about how that would affect the enhanced switch statements. But more to the point, this would create another implementation and not a mock. – Curious Jul 19 '21 at 06:46

1 Answers1

-2

It is not possible to mock a sealed class.

If you find yourself in the position of having to do so, you should be question whether or not you should be using a sealed class at all.

The actual set of use cases for sealed classes is really small and you probably don't need them.

---- original answer ----

Oh look, you found one of the various reasons that sealed classes are a thing that you should probably never use without a Very Good Reason (tm).

Sealed classes should always be abstract.

Mock one of its known implementations, not the the sealed class.

If it's not your code, find a new library.

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
lscoughlin
  • 2,327
  • 16
  • 23
  • 1
    I'm not sure this answers the question, "Is there any way to mock a sealed interface?" – Curious Jul 19 '21 at 06:48
  • 2
    @Curious how does "mock one of it's known implementations" not provide an answer? – Stultuske Jul 19 '21 at 06:58
  • Oh, you're right, this might work for my purposes. I'm going to try this quick. This however doesn't answer the question of being able to mock the _interface_. – Curious Jul 19 '21 at 07:17
  • 2
    @Curious then you should try to understand the rest of his answer, this is one of those reasons why you might not want to use sealed interfaces. anyway, by mocking an implementation, you actually are mocking the interface – Stultuske Jul 19 '21 at 07:21
  • @Stultuske This could be, but whether I want to mock a known implementation or not, this answer is a bit off topic. The question is asking if it's possible to mock a sealed class, not a specific implementation of that class. – Curious Jul 19 '21 at 07:28
  • 2
    @Curious a mock is basically an anonymous implementation, which is not covered in your permits list, as the answer states, "you found one of the variety reasons sealed classes are a thing you should probably never use without a Very Good Reason". That line basically provides the answer you claim you didn't get. – Stultuske Jul 19 '21 at 07:32
  • @Stultuske, does that mean that the answer, as written is, "You cannot mock a sealed interface"? (Does the JVM instrumentation provide support for testing operations? Are there currently other ways a mock can be created? Can I create native code to mock the interface? etc.) – Curious Jul 19 '21 at 07:34
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/235040/discussion-between-curious-and-stultuske). – Curious Jul 19 '21 at 07:45