尝试模拟时出现 Mockito 错误 cats.effect.IO

Mockito error when trying to mock cats.effect.IO

我正试图通过

来嘲笑cats.effect.IO
val ioSql: IO[Sql[IO, SqlConnection[IO]]] = mock[IO[Sql[IO, SqlConnection[IO]]]]

SqlSqlConnection 是我公司的图书馆。

我遇到了这个错误

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.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(BarSpec.scala:21)
        at org.specs2.specification.dsl.mutable.EffectBlocks.tryBlock(EffectBlocks.scala:131)
        at org.specs2.specification.dsl.mutable.EffectBlocks.$anonfun$nestBlock(EffectBlocks.scala:110)
        at org.specs2.specification.dsl.mutable.EffectBlocks.record(EffectBlocks.scala:61)
        at org.specs2.specification.dsl.mutable.MutableFragmentBuilder.$anonfun$replayFragments(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(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(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(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(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(Reporter.scala:43)
        at org.specs2.runner.Runner$.runSpecStructure(Runner.scala:109)
        at org.specs2.runner.ClassRunner.$anonfun$report(ClassRunner.scala:59)
        at org.specs2.control.eff.Arrs.go(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(Unions.scala:84)
        at org.specs2.control.eff.Arrs.go(Eff.scala:383)
        at org.specs2.control.eff.Arrs.apply(Eff.scala:399)
        at org.specs2.control.eff.Interpret$$anon.$anonfun$onEffect(Interpret.scala:53)
        at org.specs2.fp.EitherOps$.bimap$extension(EitherSyntax.scala:82)
        at org.specs2.control.eff.Interpret$$anon.onEffect(Interpret.scala:53)
        at org.specs2.control.eff.Interpret$$anon.onApplicativeEffect(Interpret.scala:61)
        at org.specs2.control.eff.Interpret$$anon.onApplicativeEffect(Interpret.scala:45)
        at org.specs2.control.eff.Interpret.go(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.call(TypeCachingBytecodeGenerator.java:37)
        at org.mockito.internal.creation.bytebuddy.TypeCachingBytecodeGenerator.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

问题是当我使用公司库的旧版本时,测试通过且没有错误。

org.typelevel.cats-effectorg.mockito.mockito-core 在我公司库的新旧版本上都是相同的版本。

做。不是。嘲笑。值。

您不要模拟字符串、布尔值或整数(我希望如此)。 ADT 属于同一类别:

  • 你不会模拟元组
  • case classes
  • objects
  • Eithers
  • Options
  • sealed traits

它们是数据。你不嘲笑数据。你传递数据。

你嘲笑行为(如果你真的无法避免)。除了它,行为是一个函数。你不模拟功能,因为它有 0 意义。您可以临时创建它并像那样传递。

而不是:

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

你可以简单地:

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
}

或者也许

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

嘲笑一切是一种愚蠢的习惯。模拟是一种减轻在测试期间为 return 创建值的负担的方法,其中替代方案是创建一个完整的假 class 实现,其中有 50 个方法,只需要其中 2 个。当您使用应被视为价值的事物时,情况并非如此。您不会使用 powermock 来检查 class 是否正确处理 String.size 抛出的情况,因为它不是设计使然。 ADT 也一样。它们可以在一个衬里中创建,它们的行为仅取决于您输入的数据。没有合理的用例来模拟、存根或伪造它们。我们正在谈论 Either、Option、Tuple、case class、enum 或 IO monad。