jmockit1 icon indicating copy to clipboard operation
jmockit1 copied to clipboard

java.lang.ClassCastException: java.lang.Class cannot be cast to {Mock method return type}

Open fdz-nelson-branco opened this issue 3 years ago • 5 comments

Please provide the following information:

  • Version of JMockit that was used: 1.35 and 1.49

  • Description of the problem or enhancement request: from time to time, we get a "java.lang.ClassCastException: java.lang.Class cannot be cast to {Mock method return type}" on either a local variable initialization or a method call argument. I couldn't yet determine a specific deterministic use case.

fdz-nelson-branco avatar Dec 21 '21 16:12 fdz-nelson-branco

Do you have a code example? What do you use MockUp or @Injectable etc?

Saljack avatar Dec 22 '21 08:12 Saljack

I'm in an effort of fixing this flaky symptom over a couple of tests. It happens on Mocked and Injectable references, much more frequent on Injectables, I've a couple of comments on the code stating that they had mocked it using Mockups due to issues using Expectations but with no further details.

One of the cases we can put it like this:

public void someTestMoethod(@Mocked final Dependency1 dependency1) {
    final long id = dependency1.getId(); // <<--- rarely it throws "java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.Long"
}

It occurs both with or without an expectation. The machine has it's CPU exhausted running the test in parallel, process forked per class, and executing sequentially over the class.

I'm looking into the source code trying to make sense of it. I spotted this exception (on a passing test) on a scheduled task that continues after the test case, this I think to be related to the mocking being "cleanup" upon the test case finishes. Also, as far as I understood, since the mocking structures aren't synchronized, we must not create expectations using multiple threads, at least without the proper synchronization.

Even so, I still can make sense of it on the case shown above.

Much appreciated your help, even if you can only guide me on where to look for on the code.

fdz-nelson-branco avatar Dec 22 '21 10:12 fdz-nelson-branco

Narrowing down a little bit more with additional details:

interface Dependency1Interface {
    Identifier getId();
}

class Dependency1 implements Dependency1Interface {}

public void someTestMethod(@Mocked final Dependency1 dependency1) {
    final Identifier id = dependency1.getId(); // <<--- rarely it throws "java.lang.ClassCastException: java.lang.Class cannot be cast to com.foo.Identifier"
}

So far I managed to reverse engineer the getId() redefined method into the following:

public getId()Ljava/foo/Identifier;
    ALOAD 0
    LDC 1025
    LDC "com/foo/Dependency1Interface"
    LDC "getId()Lcom/foo/Identifier;"
    ACONST_NULL
    LDC 0
    ACONST_NULL
    INVOKESTATIC mockit/internal/expectations/RecordAndReplayExecution.recordOrReplay (Ljava/lang/Object;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;I[Ljava/lang/Object;)Ljava/lang/Object;
    CHECKCAST com/foo/Identifier
    ARETURN
    MAXSTACK = 7
    MAXLOCALS = 1

Seems to me that this CHECKCAST com/foo/Identifier is the source of the exception. If that's true, that mean the Class instance is being returned by the RecordAndReplayExecution.recordOrReplay.

fdz-nelson-branco avatar Dec 22 '21 14:12 fdz-nelson-branco

Adding up on the findings...

I spotted that within certain circumstances, like the case where a task continues to run after the end of the test case, the RecordAndReplayExecution.recordOrReplay returns Void.class, so, this Void.class's must be on the root of the exception being thrown, now, on the getId() scenario I can't figure out why it fails from time to time...

fdz-nelson-branco avatar Dec 22 '21 15:12 fdz-nelson-branco

Why hasn't this problem been fixed yet?

zbflcy avatar Jun 30 '23 12:06 zbflcy