模拟一个期望隐式转换为泛型类型

时间:2017-12-13 16:45:32

标签: scala mockito scala-cats

我正在尝试使用Mockito模拟一个通用方法。我正在尝试模拟的方法在泛型类型ReaderT上创建T,并期望隐式转换也可用于将Output类型转换为泛型类型{{ 1}}提供。

实施并不重要,但这里减少了方法本身:

T

该方法在运行API时可以正常工作,但是,在模拟时我收到错误,然后在我的测试中执行它。我已经完成了模拟的几次迭代,这就是我现在所拥有的:

/**
 * 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]

因为我无法在模拟本身中使用通配符类型,例如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值。

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

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)) } 这样:

NullPointerException
  

可以在此PasteBin

找到完整错误

我非常感谢你解决这个问题的任何帮助!

1 个答案:

答案 0 :(得分:1)

你正在用一个名字命名的墙:“类型擦除”。当没有关于泛型的信息被维护时,Mockito纯粹在运行时运行。在使用ClassTagTypeTag的大多数情况下,有很多方法可以在scala中处理此问题,但我怀疑在mockito的情况下可以很容易地解决它。 有以下问题:

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

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