こんにちは私は私のプレゼンタークラスが呼び出すデリゲーターを使用して改造から返される単一のオブザーバブルから取得した応答を模擬しようとしています、そして私は次のエラーを得ています:
io.mockk.MockKException:回答が見つかりません:LoginPresenter(#1).login(LoginRequest([email protected]、password = password123))
これが私のテストコードです
@Test
fun testKotlinMock(){
val presenter : LoginPresenter = mockk<LoginPresenter>()
val delegator = mockk<AccountDelegatorContract>()
val viewCallback = mockk<LoginContract.LoginViewCallBack>()
val cookieStore = mockk<PianoCookieStore>()
val loginRequest = LoginRequest("[email protected]", "password123")
val customerResponse = CustomerResponse("jon", "richy")
every { delegator.login(loginRequest) } returns Single.just(Response.success(any()))
every { delegator.getCustomer() } returns Single.just(customerResponse)
every { presenter.loginViewCallBack } returns viewCallback
every { presenter.accountDelegator } returns delegator
every { presenter.cookieStorage } returns cookieStore
presenter.login(loginRequest)
}
実際のPresenterコードは次のようになります。
@Inject
lateinit var loginViewCallBack: LoginViewCallBack
@Inject
lateinit var delegator: DelegatorContract
@Inject
lateinit var cookieStorage: CookieStore
@Inject
constructor()
override fun login(loginRequest: LoginRequest) {
delegator.login(loginRequest)
.flatMap({ response ->
saveCookieAndContinue(response)
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(object : SingleObserver<CustomerResponse>{
override fun onSubscribe(d: Disposable) {
}
override fun onError(e: Throwable) {
loginViewCallBack.onErrorLogin(PianoError.ERROR_LOGIN_INVALID)
Log.d("JJJ", "login error")
}
override fun onSuccess(customerResponse : CustomerResponse) {
loginViewCallBack.onLoginSuccess(customerResponse)
Log.d("JJJ", "login successfully")
}
})
}
private fun saveCookieAndContinue(response: Response<Void>): Single<CustomerResponse> {
if (response.isSuccessful) {
val headers = response.headers()
cookieStorage.saveSessionCookies(headers.get(PianoCookieStore.COOKIE_HEADER_SET_NAME)!!)
return accountDelegator.getCustomer()
}
//TODO: Change this to throw a login exception?
throw RuntimeException()
}
私は基本的に、メインコードから挿入された依存関係を模擬し、ハッピーパスの単体テストを実行したいと考えています。
回答が見つからないというエラーでpresenter.login(loginRequest)を呼び出すと失敗します
これは私が使用しているkotlin拡張プラグインです http://mockk.io/
あなたのケースでは、テストされているクラスを模擬しています。次の2つのオプションがあります。
spyk
を使用してスパイを作成します。これは元のオブジェクトとモックの間にあるものですモックはデフォルトで厳密であるため、例外がスローされます。モックはオブジェクトがまったく初期化されていないため、モックの処理方法がわかりません。
モック、スパイ、リラックスしたモックの詳細については、こちらをご覧ください: https://blog.kotlin-academy.com/mocking-is-not-rocket-science-mockk-features-e5d55d735a98
テスト中のクラスをモックしないでください。ただし、たとえばメソッドが呼び出されたことを確認する必要がある場合は、スパイの使用は問題ありません。
私はnotを制御するクラスで注入を使用することをお勧めします。 DaggerのようなDIフレームワークは、作成しないクラス(ActivityやFragmentsなど)には最適ですが、制御するクラスにはコンストラクターを使用するだけです。
_class LoginPresenter(private val loginViewCallBack: LoginViewCallBack,
private val delegator: DelegatorContract,
private val cookieStorage: CookieStore) {
// rest of your code
}
_
これで、ログインプレゼンターにモックまたは偽物を簡単に提供できます。また、依存関係を公開しません。インジェクションを使用する場合、アクティビティから_presenter.delegator
_を呼び出すことができますが、これはおそらく望ましくありません。
サイドノート:
loginPresenterでコンストラクタを使用し、短剣を使用して、次のようなプレゼンターを作成します。
_class LoginModule {
@Provides
@ActivityScope
internal providePresenter(loginViewCallBack: LoginViewCallBack,
delegator: DelegatorContract,
cookieStorage: CookieStore): LoginPresenter = LoginPresenter(loginViewCallBack, delegator, cookieStorage)
}
_
代わりにインジェクションを使用する場合は、モックを設定することを覚えておく必要があります。
_@Test
fun `test authentication fails`() {
val loginViewCallBack = mockk<LoginViewCallBack>()
val delegator = mockk<DelegatorContract>()
val cookieStorage = mockk<CookieStore>()
val presenter = LoginPresenter()
presenter.loginViewCallBack = loginViewCallBack
presenter.delegator = delegator
presenter.cookieStorage = cookieStorage
val loginRequest: LoginRequest = ... //mock, fake, or real object
every { delegator.login(loginRequest) } returns Single.error(RuntimeException("oops!"))
presenter.login(loginRequest)
verify { loginViewCallBack.onErrorLogin(PianoError.ERROR_LOGIN_INVALID) }
}
_
上記の例は、presenter
がモックではなくなったため、presenter.login(request)
の「応答が見つかりません」を解消します。
時々エラーはあなたがばかげたことを逃したためです。私の場合、リクエストクラスとレスポンスクラスで「データクラス」を使用するのに失敗しました。それだけでした。 「relaxedMockks」や「スパイ」などについては何もありません。しかし、メッセージはあなたが持っているのと同じでした:「答えが見つかりません」。
LoginRequestとCustomerResponseがデータクラスであることを確認します。
まず、テストをデバッグすることをお勧めします。次に、実行に失敗したコードの行を見つけます。私もあなたと同じ経験をしましたが、私の場合、コードが次のようにonSuccess
に達したときにテストが失敗しました。
_override fun onSuccess(customerResponse : CustomerResponse) {
loginViewCallBack.onLoginSuccess(customerResponse)
Log.d("JJJ", "login successfully")
}
_
モックテストでloginViewCallback
が見つからないため、loginViewCallBack.onLoginSuccess(customerResponse)
行に達した後、テストは失敗すると思います。モックしたいインターフェイスクラスがある場合は、次のように記述します。
_@RelaxedMockK
lateinit var viewCallback: LoginContract.LoginViewCallBack
_
私の場合、リラックスしたモックでこのインターフェイスを変更した後、エラー_not answer found error
_が解決しました。
docs から:Relaxed mockは、すべての関数に対して単純な値を返すモックです。これにより、各ケースの動作の指定をスキップしながら、必要なものをスタブすることができます。参照型の場合、連鎖モックが返されます。