Search code examples
scalamockitocats-effect

Mockito error when trying to mock cats.effect.IO


I'm trying to mock cats.effect.IO by

val ioSql: IO[Sql[IO, SqlConnection[IO]]] = mock[IO[Sql[IO, SqlConnection[IO]]]]

Sql and SqlConnection are my company libraries.

I've got this error

Underlying exception : java.lang.reflect.MalformedParameterizedTypeException
------------------------------------------------------------
    org.mockito.exceptions.base.MockitoException: 
    Mockito cannot mock this class: class cats.effect.IO.
    
    Mockito can only mock non-private & non-final classes.
    If you're not sure why you're getting this error, please report to the mailing list.
    
    
    Java               : 1.8
    JVM vendor name    : Azul Systems, Inc.
    JVM vendor version : 25.192-b01
    JVM name           : OpenJDK 64-Bit Server VM
    JVM version        : 1.8.0_192-b01
    JVM info           : mixed mode
    OS name            : Windows 10
    OS version         : 10.0
    
    
    Underlying exception : java.lang.reflect.MalformedParameterizedTypeException
        at org.specs2.mock.MockitoMocker.mock(MockitoMocker.scala:21)
        at org.specs2.mock.MockitoMocker.mock$(MockitoMocker.scala:21)
        at org.specs2.mock.mockito.TheMockitoMocker$$anon$1.mock(TheMockitoMocker.scala:8)
        at org.specs2.mock.mockito.MocksCreation.mock(MocksCreation.scala:18)
        at org.specs2.mock.mockito.MocksCreation.mock$(MocksCreation.scala:18)
        at com.foo.BarSpec.mock(BarSpec.scala:15)
        at com.goo.BarSpec.$anonfun$new$1(BarSpec.scala:21)
        at org.specs2.specification.dsl.mutable.EffectBlocks.tryBlock(EffectBlocks.scala:131)
        at org.specs2.specification.dsl.mutable.EffectBlocks.$anonfun$nestBlock$2(EffectBlocks.scala:110)
        at org.specs2.specification.dsl.mutable.EffectBlocks.record(EffectBlocks.scala:61)
        at org.specs2.specification.dsl.mutable.MutableFragmentBuilder.$anonfun$replayFragments$2(MutableFragmentBuilder.scala:47)
        at scala.Option.getOrElse(Option.scala:189)
        at org.specs2.specification.dsl.mutable.MutableFragmentBuilder.replayFragments(MutableFragmentBuilder.scala:47)
        at org.specs2.specification.dsl.mutable.MutableFragmentBuilder.specificationFragments(MutableFragmentBuilder.scala:37)
        at org.specs2.specification.dsl.mutable.MutableFragmentBuilder.specificationFragments$(MutableFragmentBuilder.scala:36)
        at org.specs2.mutable.Specification.specificationFragments(Specification.scala:15)
        at org.specs2.specification.dsl.mutable.MutableFragmentBuilder.$anonfun$is$1(MutableFragmentBuilder.scala:44)
        at org.specs2.specification.core.SpecStructure.fragments$lzycompute(SpecStructure.scala:26)
        at org.specs2.specification.core.SpecStructure.fragments(SpecStructure.scala:26)
        at org.specs2.specification.core.SpecStructure.$anonfun$map$1(SpecStructure.scala:29)
        at org.specs2.specification.core.SpecStructure.fragments$lzycompute(SpecStructure.scala:26)
        at org.specs2.specification.core.SpecStructure.fragments(SpecStructure.scala:26)
        at org.specs2.specification.core.SpecStructure.$anonfun$$bar$greater$1(SpecStructure.scala:30)
        at org.specs2.specification.core.SpecStructure.fragments$lzycompute(SpecStructure.scala:26)
        at org.specs2.specification.core.SpecStructure.fragments(SpecStructure.scala:26)
        at org.specs2.specification.core.SpecStructure.$anonfun$$bar$greater$1(SpecStructure.scala:30)
        at org.specs2.specification.core.SpecStructure.fragments$lzycompute(SpecStructure.scala:26)
        at org.specs2.specification.core.SpecStructure.fragments(SpecStructure.scala:26)
        at org.specs2.specification.core.SpecStructure.contents(SpecStructure.scala:28)
        at org.specs2.reporter.Reporter.$anonfun$report$1(Reporter.scala:43)
        at org.specs2.runner.Runner$.runSpecStructure(Runner.scala:109)
        at org.specs2.runner.ClassRunner.$anonfun$report$2(ClassRunner.scala:59)
        at org.specs2.control.eff.Arrs.go$1(Eff.scala:386)
        at org.specs2.control.eff.Arrs.apply(Eff.scala:399)
        at org.specs2.control.eff.Arrs.apply(Eff.scala:348)
        at org.specs2.control.eff.CollectedUnions.$anonfun$continuation$1(Unions.scala:84)
        at org.specs2.control.eff.Arrs.go$1(Eff.scala:383)
        at org.specs2.control.eff.Arrs.apply(Eff.scala:399)
        at org.specs2.control.eff.Interpret$$anon$1.$anonfun$onEffect$1(Interpret.scala:53)
        at org.specs2.fp.EitherOps$.bimap$extension(EitherSyntax.scala:82)
        at org.specs2.control.eff.Interpret$$anon$1.onEffect(Interpret.scala:53)
        at org.specs2.control.eff.Interpret$$anon$1.onApplicativeEffect(Interpret.scala:61)
        at org.specs2.control.eff.Interpret$$anon$1.onApplicativeEffect(Interpret.scala:45)
        at org.specs2.control.eff.Interpret.go$1(Interpret.scala:200)
        at org.specs2.control.eff.Interpret.interpretLoop(Interpret.scala:207)
        at org.specs2.control.eff.Interpret.interpretLoop$(Interpret.scala:142)
        at org.specs2.control.eff.Interpret$.interpretLoop(Interpret.scala:635)
        at org.specs2.control.eff.Interpret.interpret(Interpret.scala:71)
        at org.specs2.control.eff.Interpret.interpret$(Interpret.scala:44)
        at org.specs2.control.eff.Interpret$.interpret(Interpret.scala:635)
        at org.specs2.control.eff.Interpret.interpret1(Interpret.scala:78)
        at org.specs2.control.eff.Interpret.interpret1$(Interpret.scala:77)
        at org.specs2.control.eff.Interpret$.interpret1(Interpret.scala:635)
        at org.specs2.control.eff.ErrorInterpretation.runError(ErrorEffect.scala:87)
        at org.specs2.control.eff.ErrorInterpretation.runError$(ErrorEffect.scala:68)
        at org.specs2.control.eff.ErrorEffect$.runError(ErrorEffect.scala:187)
        at org.specs2.control.eff.syntax.error$ErrorEffectOps.runError(error.scala:14)
        at org.specs2.control.ExecuteActions.attemptExecuteAction(ExecuteActions.scala:59)
        at org.specs2.control.ExecuteActions.attemptExecuteAction$(ExecuteActions.scala:56)
        at org.specs2.control.ExecuteActions$.attemptExecuteAction(ExecuteActions.scala:93)
        at org.specs2.runner.Runner$.execute(Runner.scala:28)
        at org.specs2.runner.ClassRunner.run(ClassRunner.scala:46)
        at org.specs2.runner.ClassRunner.run$(ClassRunner.scala:31)
        at org.specs2.runner.ClassRunner$.run(ClassRunner.scala:79)
        at org.specs2.runner.ClassRunner.run(ClassRunner.scala:25)
        at org.specs2.runner.ClassRunner.run$(ClassRunner.scala:24)
        at org.specs2.runner.ClassRunner$.run(ClassRunner.scala:79)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.jetbrains.plugins.scala.testingSupport.specs2.Specs2Runner.runWithNotifierRunner(Specs2Runner.java:154)
        at org.jetbrains.plugins.scala.testingSupport.specs2.Specs2Runner.runSpecs2_new(Specs2Runner.java:144)
        at org.jetbrains.plugins.scala.testingSupport.specs2.Specs2Runner.main(Specs2Runner.java:40)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at com.intellij.rt.execution.CommandLineWrapper.main(CommandLineWrapper.java:63)
    Caused by: java.lang.reflect.MalformedParameterizedTypeException
        at sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.validateConstructorArguments(ParameterizedTypeImpl.java:58)
        at sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.<init>(ParameterizedTypeImpl.java:51)
        at sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.make(ParameterizedTypeImpl.java:92)
        at sun.reflect.generics.factory.CoreReflectionFactory.makeParameterizedType(CoreReflectionFactory.java:105)
        at sun.reflect.generics.visitor.Reifier.visitClassTypeSignature(Reifier.java:140)
        at sun.reflect.generics.tree.ClassTypeSignature.accept(ClassTypeSignature.java:49)
        at sun.reflect.generics.repository.MethodRepository.getReturnType(MethodRepository.java:68)
        at java.lang.reflect.Method.getGenericReturnType(Method.java:255)
        at net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection$ForLoadedReturnType.resolve(TypeDescription.java:6512)
        at net.bytebuddy.description.type.TypeDescription$Generic$LazyProjection.accept(TypeDescription.java:6106)
        at net.bytebuddy.description.method.MethodDescription$TypeSubstituting.getReturnType(MethodDescription.java:1607)
        at net.bytebuddy.description.method.MethodDescription$AbstractBase.asSignatureToken(MethodDescription.java:838)
        at net.bytebuddy.matcher.SignatureTokenMatcher.matches(SignatureTokenMatcher.java:47)
        at net.bytebuddy.matcher.SignatureTokenMatcher.matches(SignatureTokenMatcher.java:26)
        at net.bytebuddy.matcher.ElementMatcher$Junction$Conjunction.matches(ElementMatcher.java:122)
        at net.bytebuddy.matcher.FilterableList$AbstractBase.filter(FilterableList.java:125)
        at net.bytebuddy.dynamic.scaffold.subclass.SubclassImplementationTarget.invokeConstructor(SubclassImplementationTarget.java:76)
        at net.bytebuddy.dynamic.scaffold.subclass.SubclassImplementationTarget.invokeSuper(SubclassImplementationTarget.java:62)
        at net.bytebuddy.implementation.Implementation$Target$AbstractBase.invokeDominant(Implementation.java:442)
        at net.bytebuddy.implementation.SuperMethodCall$Appender.apply(SuperMethodCall.java:130)
        at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyCode(TypeWriter.java:713)
        at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod$WithBody.applyBody(TypeWriter.java:698)
        at net.bytebuddy.dynamic.scaffold.TypeWriter$MethodPool$Record$ForDefinedMethod.apply(TypeWriter.java:605)
        at net.bytebuddy.dynamic.scaffold.TypeWriter$Default$ForCreation.create(TypeWriter.java:5133)
        at net.bytebuddy.dynamic.scaffold.TypeWriter$Default.make(TypeWriter.java:1933)
        at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:225)
        at net.bytebuddy.dynamic.scaffold.subclass.SubclassDynamicTypeBuilder.make(SubclassDynamicTypeBuilder.java:198)
        at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase.make(DynamicType.java:3404)
        at net.bytebuddy.dynamic.DynamicType$Builder$AbstractBase$Delegator.make(DynamicType.java:3600)
        at org.mockito.internal.creation.bytebuddy.SubclassBytecodeGenerator.mockClass(SubclassBytecodeGenerator.java:173)
        at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$1.call(TypeCachingBytecodeGenerator.java:37)
        at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator$1.call(TypeCachingBytecodeGenerator.java:34)
        at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:152)
        at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:365)
        at net.bytebuddy.TypeCache.findOrInsert(TypeCache.java:174)
        at net.bytebuddy.TypeCache$WithInlineExpunction.findOrInsert(TypeCache.java:376)
        at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.mockClass(TypeCachingBytecodeGenerator.java:32)
        at org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.createMockType(SubclassByteBuddyMockMaker.java:71)
        at org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.createMock(SubclassByteBuddyMockMaker.java:42)
        at org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker.createMock(ByteBuddyMockMaker.java:25)
        at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:35)
        at org.mockito.internal.MockitoCore.mock(MockitoCore.java:63)
        at org.mockito.Mockito.mock(Mockito.java:1908)
        at org.mockito.Mockito.mock(Mockito.java:1817)
        ... 79 more

The question is when I used the older version of my company libraries, the tests pass with no error.

Both of org.typelevel.cats-effect and org.mockito.mockito-core are the same version on both older and newer version of my company libraries.


Solution

  • Do. Not. Mock. Values.

    You don't mock Strings, Booleans or Ints (I hope). ADTs are in the same category:

    • you don't mock tuples
    • case classes
    • objects
    • Eithers
    • Options
    • sealed traits

    They are data. You don't mock data. You pass data.

    You mock behavior (if you cannot really avoid it). Except it the behavior is a function. You do not mock function, because it makes 0 sense. You can create it ad hoc and pass just like that.

    Instead of:

    val ioSql: IO[Sql[IO, SqlConnection[IO]]] = mock[IO[Sql[IO, SqlConnection[IO]]]]
    

    you could simply:

    val ioSql: IO[Sql[IO, SqlConnection[IO]]] = IO {
      // here create Sql[IO, SqlConnection[IO]]] value
      // I mean if you want to mock it you virtually have to create it anyway
      // you can even put side effects here if you want to check that
      // IO was evaluated or sth
    }
    

    or maybe

    val ioSql: IO[Sql[IO, SqlConnection[IO]]] = IO.raiseError(
      new Exception("No SQL for you")
    )
    

    Mocking everything is a foolish habit. Mocking is a way of lessen the burden of creating a value to return during tests in where the alternative would be to create a whole fake class implementation where there is 50 methods and only 2 of them are needed. It's not the case when you are using things that should be treated as values. You wouldn't use powermock to check if you class handles properly the case where String.size throws, because it doesn't by design. Same for ADTs. They can be create in one liner, their hebavior depends only of what data you put it. There is no reasonable use case for mocking, stubbing or faking them if. It true ether we are talking about Either, Option, Tuple, case class, enum or IO monad.