mockito / mockito

Most popular Mocking framework for unit tests written in Java
http://mockito.org
MIT License
14.81k stars 2.55k forks source link

`mockStatic` fails with `class redefinition failed: invalid class` #3273

Open scordio opened 6 months ago

scordio commented 6 months ago

Mockito message in the stack trace:

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:167)
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.triggerRetransformation(InlineBytecodeGenerator.java:281)
    at org.mockito.internal.creation.bytebuddy.InlineBytecodeGenerator.mockClassStatic(InlineBytecodeGenerator.java:226)
    at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClassStatic(TypeCachingBytecodeGenerator.java:108)
    at org.mockito.internal.creation.bytebuddy.InlineDelegateByteBuddyMockMaker.createStaticMock(InlineDelegateByteBuddyMockMaker.java:592)
    at org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker.createStaticMock(InlineByteBuddyMockMaker.java:83)
    at org.mockito.internal.util.MockUtil.createStaticMock(MockUtil.java:202)
    at org.mockito.internal.MockitoCore.mockStatic(MockitoCore.java:134)
    at org.mockito.Mockito.mockStatic(Mockito.java:2328)
    at org.mockito.Mockito.mockStatic(Mockito.java:2265)
    at <line of try block in test method>

The problematic class belongs to the Thales JCPROV library which is unfortunately not available on Maven Central so I cannot compose a reproducer.

However, I can describe its characteristics:

As the native library is not available during test execution (and I cannot make it available), I would guess that the failure in the static block triggers the exception above. However, I don't know how to prove it.

Is there anything I could do to properly mock such a class? Otherwise, do you see any possible improvements in Mockito for these cases?

Please, let me know in case I could grab additional details to help the analysis.

The issue happens on Mockito 5.10.0 and OpenJDK 11.0.12.

jkim323 commented 6 months ago

Although I am unfamiliar to the the Thales JCPROV library, I am a bit curious on this issue. I am wondering if you have tried checking if your class is missing in your class path by simply trying to use the static method before mocking it because this has happened to me once before. But based on your issue since you are work involves with native code maybe mocking the native methods is not the best route because I have heard of challenges on doing this. Have you tried exploring doing something like encapsulating it in a wrapper class or in an interface?

scordio commented 6 months ago

Yes, the library is in the classpath: if I call one of the methods without mocking, it fails in the static initialization block with a missing native library error, as expected.

Also yes, wrapping works fine as a workaround. In addition, there is another class (still part of the library) that offers similar static methods, wrapping each native call with exception throwing in case of errors, and mocking this class works as expected.

jkim323 commented 6 months ago

Interesting. Does the other successful class also share the same characteristics as the class you are having problems with or are there differences? I am wondering if two classes share the last bullet you mentioned in the post. Also, have you explored using PowerMock perhaps?

scordio commented 6 months ago

No, they are not the same. The successful one:

Imagine something like:

public class NativeClass {

  private NativeClass() {}

  public static native int method();

  static {
    System.loadLibrary("native");
  }

}

public class WrapperClass {

  private WrapperClass() {}

  public static int method() {
    int result = NativeClass.method();
    if (result != 0) throw new RuntimeException(result);
    return 0;
  }

}

(written without checking the original code – beware of typos 🙂 )

jkim323 commented 6 months ago

After reading your difference , it makes sense to why you are seeing it fail because it seems like a common known issue that Mockito is unable to mock native methods; it is currently a limitation on Mockito as of now. Perhaps you can try PowerMock as an alternative solution but I am not aware of a definitive solution.. unless we develop on this maybe creating a discussion on this topic might help.