Mockito cannot mock this class: class scala.xml.Elem
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
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.