Skip to content

Mockito fails with JVM internal error when mocking class clinit does not find a transitive dependency #3498

@AndreasTu

Description

@AndreasTu

When tying to mock a class, where some transitive dependencies from the <clinit> are missing, Mockito swallows the root cause NoClassDefFoundError, but instead is reporting a JVM internal error
java.lang.InternalError: class redefinition failed: invalid class, because the failing class is still tried to retransform.

Mockito reports the following exception:

org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: class org.mockitousage.bugs.creation.MockClassWithMissingStaticDepTest$ClassWithErrorInCinit.
Most likely it is a private class that is not visible by Mockito

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.

	at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.prettifyFailure(InlineDelegateByteBuddyMockMaker.java:451)
	at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.createMockType(InlineDelegateByteBuddyMockMaker.java:416)
	at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.doCreateMock(InlineDelegateByteBuddyMockMaker.java:367)
	at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.createMock(InlineDelegateByteBuddyMockMaker.java:346)
	at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createMock(InlineByteBuddyMockMaker.java:56)
	at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:99)
	at org.mockito.internal.MockitoCore.mock(MockitoCore.java:84)
	at org.mockito.Mockito.mock(Mockito.java:2162)
	at org.mockito.Mockito.mock(Mockito.java:2077)
	at org.mockitousage.bugs.creation.MockClassWithMissingStaticDepTest.shouldNotSwallowCinitErrorsOfClassToMock(MockClassWithMissingStaticDepTest.java:21)
Caused by: java.lang.IllegalArgumentException: Could not create type
	at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:170)
	at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:399)
	at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:190)
	at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:410)
	at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClass(TypeCachingBytecodeGenerator.java:75)
	at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.createMockType(InlineDelegateByteBuddyMockMaker.java:408)
	... 47 more
Caused by: java.lang.InternalError: class redefinition failed: invalid class
	at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses0(Native Method)
	at java.instrument/sun.instrument.InstrumentationImpl.retransformClasses(InstrumentationImpl.java:225)
	at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:285)
	at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.mockClass(InlineBytecodeGenerator.java:218)
	at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.lambda$mockClass$0(TypeCachingBytecodeGenerator.java:78)
	at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:168)
	... 52 more

Test to reporduce the issue:

public class MockClassWithMissingStaticDepTest {

    @Test
    public void shouldNotSwallowCinitErrorsOfClassToMock() {
        try {
            @SuppressWarnings("unused")
            var unused = Mockito.mock(ClassWithErrorInClinit.class);

            fail();
        } catch (MockitoException e) {
            e.printStackTrace();
            var cause = e.getCause();
            assertThat(cause).isInstanceOf(MockitoException.class);
            assertThat(cause.getMessage()).isEqualTo("Cannot instrument class org.mockitousage.bugs.creation.MockClassWithMissingStaticDepTest$ClassWithErrorInCinit because it or one of its supertypes could not be initialized");

            var cause2 = cause.getCause();
            assertThat(cause2).isInstanceOf(NoClassDefFoundError.class);
            assertThat(cause2.getMessage()).isEqualTo("Simulate missing transitive dependency used in <clinit>.");
        }
    }


    private static class ClassWithErrorInClinit {
        static {
            //noinspection ConstantValue
            if (true) {
                throw new NoClassDefFoundError("Simulate missing transitive dependency used in <clinit>.");
            }
        }
    }
}

Root-Cause

The method org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.assureInitialization() only handles ExceptionInInitializerError and swallows everything else.

Expected behavior

Mockito shall report the first NoClassDefFoundError instead of swallowing it.

Affected version: 5.14.2
Affected JVM: 21

I will open a PR to fix the issue.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      pFad - Phonifier reborn

      Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

      Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


      Alternative Proxies:

      Alternative Proxy

      pFad Proxy

      pFad v3 Proxy

      pFad v4 Proxy