2017-12-13 12 views
1

私は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.<?>値を現在使用していることに大きく依存しているためです。このモック実装は限り明確に行くための最良の方法ですが、

// 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は、このような一般的なメソッドに渡されるタイプを一致させることができたことを期待したい。この貧しいアプローチを避けるために

私は、Mockitoは最初のモックを無視するだけです(2番目のモックをオーバーライドしますか?)ので、汎用メソッドに渡される予想される出力タイプと正しく一致しません。私は非常にある任意の助けをいただければと思います

[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) 

フルエラーがこのPasteBin

で見つけることができます:私の最善の努力にもかかわらず

、両方の実装は、このようなNullPointerExceptionにつながりますこの問題を解決する!

答えて

1

「名前をタイプする」という名前の壁に当たっています。ジェネリックに関する情報が維持されていない場合、Mockitoは実行時に純粋に動作します。 ClassTagまたはTypeTagを使って、ほとんどの場合スカラーでこの問題に対処する方法がありますが、それはmockitoの場合には簡単に解決できるとは思えません。 次のような問題があります:

Mockitoはそう型消去のいずれかの回避策は、次の点を考慮し、直接それらのaditional暗黙に合格する必要があり、メソッド呼び出しのスタックを使用して、実行時にモックを登録します。

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 mockのようないくつかのスケーラ固有の解決策を試してみてください。

関連する問題