mockito-scala icon indicating copy to clipboard operation
mockito-scala copied to clipboard

Mockito cannot mock this class: class scala.xml.Elem

Open glidester opened this issue 2 years ago • 1 comments

I am upgrading an old service that had fallen behind on lib updates.

After upgrading to "mockito-scala ->1.17.29" (and even tried specifying "mockito-core -> 2.22.0") I am now seeing this mockito error message when running my tests.

What is odd is that I'm not asking mockito to mock an instance of scala.xml.Elem, just to mock a class method that returns one.

Here is a minimal example of this issue:

import org.mockito.MockitoSugar
import org.scalatest.matchers.must.Matchers
import org.scalatest.wordspec.AnyWordSpec

import scala.xml.Elem

trait ReturnsXml {
  def respond(): Elem
}

class IssueSpec extends AnyWordSpec with Matchers with MockitoSugar {
  val generator = mock[ReturnsXml]

  "Mockito" should {
    "be able to mock a class" that {
      "returns a scala.xml value" in {
        when(generator.respond()).thenReturn(<empty/>)

        generator.respond() mustBe <empty/>
      }
    }
  }
}

And here is the runtime error:

[info]   - returns a scala.xml value *** FAILED ***
[info]     org.mockito.exceptions.base.MockitoException: Mockito cannot mock this class: class scala.xml.Elem.
[info] 
[info] Mockito can only mock non-private & non-final classes.
[info] If you're not sure why you're getting this error, please report to the mailing list.
[info] 
[info] 
[info] Java               : 11
[info] JVM vendor name    : AdoptOpenJDK
[info] JVM vendor version : 11.0.9.1+1
[info] JVM name           : OpenJDK 64-Bit Server VM
[info] JVM version        : 11.0.9.1+1
[info] JVM info           : mixed mode
[info] OS name            : Linux
[info] OS version         : 5.4.0-167-generic
[info] 
[info] 
[info] Underlying exception : java.lang.IllegalStateException: java.lang.VerifyError: class scala.xml.Elem$MockitoMock$MKld2yOT overrides final method scala.collection.AbstractSeq.concat(Lscala/collection/IterableOnce;)Ljava/lang/Object;
[info]     at scala.Option.orElse(Option.scala:477)
[info]     at IssueSpec.$anonfun$new$3(IssueSpec.scala:22)
[info]     at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
[info]     at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
[info]     at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
[info]     at org.scalatest.Transformer.apply(Transformer.scala:22)
[info]     at org.scalatest.Transformer.apply(Transformer.scala:20)
[info]     at org.scalatest.wordspec.AnyWordSpecLike$$anon$3.apply(AnyWordSpecLike.scala:1076)
[info]     at org.scalatest.TestSuite.withFixture(TestSuite.scala:196)
[info]     at org.scalatest.TestSuite.withFixture$(TestSuite.scala:195)
[info]     ...
[info]     Cause: java.lang.IllegalStateException: java.lang.VerifyError: class scala.xml.Elem$MockitoMock$MKld2yOT overrides final method scala.collection.AbstractSeq.concat(Lscala/collection/IterableOnce;)Ljava/lang/Object;
[info]     at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$UsingUnsafeInjection.defineClass(ClassInjector.java:1045)
[info]     at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection.injectRaw(ClassInjector.java:284)
[info]     at net.bytebuddy.dynamic.loading.ClassInjector$AbstractBase.inject(ClassInjector.java:118)
[info]     at net.bytebuddy.dynamic.loading.ClassLoadingStrategy$Default$InjectionDispatcher.load(ClassLoadingStrategy.java:241)
[info]     at net.bytebuddy.dynamic.TypeResolutionStrategy$Passive.initialize(TypeResolutionStrategy.java:101)
[info]     at net.bytebuddy.dynamic.DynamicType$Default$Unloaded.load(DynamicType.java:6166)
[info]     at org.mockito.internal.creation.bytebuddy.SubclassBytecodeGenerator.mockClass(SubclassBytecodeGenerator.java:289)
[info]     at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.lambda$mockClass$0(TypeCachingBytecodeGenerator.java:47)
[info]     at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:168)
[info]     at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:399)
[info]     ...
[info]     Cause: java.lang.VerifyError: class scala.xml.Elem$MockitoMock$MKld2yOT overrides final method scala.collection.AbstractSeq.concat(Lscala/collection/IterableOnce;)Ljava/lang/Object;
[info]     at java.base/java.lang.ClassLoader.defineClass1(Native Method)
[info]     at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1017)
[info]     at java.base/java.lang.ClassLoader$ByteBuddyAccessor$V1.defineClass(Unknown Source)
[info]     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[info]     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[info]     at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[info]     at java.base/java.lang.reflect.Method.invoke(Method.java:566)
[info]     at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection$Dispatcher$UsingUnsafeInjection.defineClass(ClassInjector.java:1041)
[info]     at net.bytebuddy.dynamic.loading.ClassInjector$UsingReflection.injectRaw(ClassInjector.java:284)
[info]     at net.bytebuddy.dynamic.loading.ClassInjector$AbstractBase.inject(ClassInjector.java:118)

Can someone advise if this is a bug or am I missing something?

Thanks

glidester avatar Nov 27 '23 09:11 glidester

A kind soul gave me a work around for this issue on stackoverflow: https://stackoverflow.com/questions/77556227/mockito-scala-mockito-cannot-mock-this-class-class-scala-xml-elem

The work around was to use the following style initialisation: doReturn(<empty/>).when(generator).respond()

This works but it is not obvious why it works and the other does not. I'm leaving this issue open as it would still be good to fix the original style bug, or at the very least improve the generated error to include suggesting using this alternate style.

glidester avatar Nov 28 '23 08:11 glidester