模拟需要隐式转换为泛型类型的函数



我试图用Mockito模拟一个通用方法。我试图模拟的方法在泛型类型T上创建了一个ReaderT,并期望还可以进行隐式转换,以将Output类型转换为T提供的泛型类型。

实现并不重要,但这里是方法本身的简化:

/**
* The return type of WebReaderT is held within the class scope.
* It pre-populates some of the types for ReaderT.
*/
def createToken[T](authRequest: Input, tokenTtl: TokenTtlConfig)(implicit f: Output => T): WebReaderT[T]

该方法在运行 API 时按我的预期工作,但是,我在模拟然后在测试中执行它时收到错误。我已经经历了几次模拟迭代,这就是我目前拥有的:

when(mock.createToken[Any](any[AuthAdapter.Input], any[TokenTtlConfig])(any[AuthAdapter.Output => Any])) thenAnswer { invocation =>
val tokenTtl = invocation.getArgument[TokenTtlConfig](1)
tokenTtl match {
case config.tokenTtlMap.v0Tokens => mockCreateToken[LoginResponse](tokenTtl)
case config.tokenTtlMap.v1Tokens => mockCreateToken[AccessTokenResponse](tokenTtl)
}
}
/**
* This method is functionally a direct copy of the method that
* it's effectively mocking.
*/
def mockCreateToken[T](tokenTtl: TokenTtlConfig)(implicit f: AuthAdapter.Output => T): WebReaderT[T] = {
ReaderT.lift[EitherTError, SentinelEnv[Future], T](EitherT.fromEither[Future](Right(AuthAdapter.Output(
mockUser1._id,
mockUser1._id,
tokenTtl.accessTtl.map(AccessToken(DateTime.now, _, "foo")),
tokenTtl.refreshTtl.map(RefreshToken(DateTime.now, _, "bar"))
))))
}

因为我无法在模拟本身中使用通配符类型,例如T,所以我必须与Any匹配,然后通过将tokenTtl与一组已知值匹配来确定预期的输出类型。对我来说,这显然是一种非常可疑的确定输出类型的方法,因为它在很大程度上依赖于应用程序当前在内部使用config.tokenTtlMap.<?>值。

为了避免这种糟糕的方法,我希望 Mockito 能够匹配传递给泛型方法的类型,如下所示:

// Only match "LoginResponse"
when(mockAuthAdapter.createToken[LoginResponse](any[AuthAdapter.Input], any[TokenTtlConfig])(any[AuthAdapter.Output => LoginResponse])) thenAnswer { invocation =>
mockCreateToken[LoginResponse](invocation.getArgument[TokenTtlConfig](1))
}
// Only match "AccessTokenResponse"
when(mockAuthAdapter.createToken[AccessTokenResponse](any[AuthAdapter.Input], any[TokenTtlConfig])(any[AuthAdapter.Output => AccessTokenResponse])) thenAnswer { invocation =>
mockCreateToken[AccessTokenResponse](invocation.getArgument[TokenTtlConfig](1))
}

这个模拟实现显然是最好的方法,但据我所知,Mockito只是忽略了第一个模拟(第二个模拟会覆盖它吗?),因此不能正确匹配传递给泛型方法的预期输出类型。

尽管我尽了最大的努力,但这两种实现都会导致这样的NullPointerException

[ERROR] [12/13/2017 14:42:05.587] [specs2.fixed.env-1062542254-1] [akka.actor.ActorSystemImpl(io-ctek-services-sentinel-routes-v0-TokensRouteSpec)] Error during processing of request: 'java.lang.NullPointerException (No error message supplied)'. Completing with 500 Internal Server Error response. To change default exception handling behavior, provide a custom ExceptionHandler.
java.lang.NullPointerException
at cats.data.EitherTFunctions$FromEitherPartiallyApplied.apply(EitherT.scala:277)
at io.ctek.services.sentinel.helpers.RouteHelpers$.createToken(RouteHelpers.scala:49)
at io.ctek.services.sentinel.helpers.RouteHelpers$.$anonfun$new$1(RouteHelpers.scala:43)
at org.mockito.internal.stubbing.StubbedInvocationMatcher.answer(StubbedInvocationMatcher.java:35)
at org.mockito.internal.handler.MockHandlerImpl.handle(MockHandlerImpl.java:95)
at org.mockito.internal.handler.NullResultGuardian.handle(NullResultGuardian.java:32)
at org.mockito.internal.handler.InvocationNotifierHandler.handle(InvocationNotifierHandler.java:36)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:57)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor.doIntercept(MockMethodInterceptor.java:43)
at org.mockito.internal.creation.bytebuddy.MockMethodInterceptor$DispatcherDefaultingToRealMethod.interceptSuperCallable(MockMethodInterceptor.java:119)

完整的错误可以在此粘贴箱中找到

我将非常感谢解决此问题的任何帮助!

你碰壁了,名字是:"键入擦除"。Mockito纯粹在运行时运行,当不维护有关泛型的信息时。在大多数情况下,使用ClassTagTypeTag在 scala 中有一些方法可以解决这个问题,但我怀疑在 mockito 的情况下是否可以轻松解决。 存在以下问题:

Mockito 使用方法调用堆栈在运行时注册 mock,因此任何类型的擦除解决方法都需要直接传递这些附加隐式,请考虑以下事项:

def myMethod[A](argument: Input): Output
//in tests
when(mock.myMethod[Int](any[Input])) thanAnswer {???}
when(mock.myMethod[Int](any[Input])) thanAnswer {???}

第二个定义首先覆盖,正如您正确观察到的那样。因此,我们可以尝试通过使用 ClassTags 来解决此问题:

def myMethod[A:ClassTag](argument: Input): Output
//in tests
when(mock.myMethod[Int](any[Input])) thanAnswer {???}
when(mock.myMethod[Double](any[Input])) thanAnswer {???}

隐式类标签参数现在怎么样?首先,我不确定 mockito 将如何与它互动。其次,你用不必要的隐式参数污染你的方法签名,只是因为它是编写测试所必需的。

您可以尝试使用一些特定于 scala 的解决方案,例如 scala mock。

相关内容

  • 没有找到相关文章

最新更新