Search code examples
scalatestingmockito

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


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.

If I wrap the returned Elem object in a case class and return that in a method mockito is happy!

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
  def respondWrapped(): Wrapper
}

case class Wrapper(value: Elem)

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

  "Mockito" should {
    "be able to mock a class" that {
      "returns a wrapped scala.xml value" in {
        /* WORKS */
        when(generator.respondWrapped()).thenReturn(Wrapper(<empty/>))
        generator.respondWrapped() mustBe Wrapper(<empty/>)
      }

      "returns a scala.xml value" in {
        /* ERRORS */
        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

P.S. Also raised this as an issue on the github repo: https://github.com/mockito/mockito-scala/issues/518


Solution

  • you have to change

    when(generator.respond()).thenReturn(<empty/>)
    

    for

    doReturn(<empty/>).when(generator).respond()