web-dev-qa-db-ja.com

kotlin mockkが見つからないエラーに答えません

こんにちは私は私のプレゼンタークラスが呼び出すデリゲーターを使用して改造から返される単一のオブザーバブルから取得した応答を模擬しようとしています、そして私は次のエラーを得ています:

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/

9
Jonathan

あなたのケースでは、テストされているクラスを模擬しています。次の2つのオプションがあります。

  • loginPresenterのmockkを削除し、元のオブジェクトを使用してプロパティを設定する
  • spykを使用してスパイを作成します。これは元のオブジェクトとモックの間にあるものです

モックはデフォルトで厳密であるため、例外がスローされます。モックはオブジェクトがまったく初期化されていないため、モックの処理方法がわかりません。

モック、スパイ、リラックスしたモックの詳細については、こちらをご覧ください: https://blog.kotlin-academy.com/mocking-is-not-rocket-science-mockk-features-e5d55d735a98

4
oleksiyp

テスト中のクラスをモックしないでください。ただし、たとえばメソッドが呼び出されたことを確認する必要がある場合は、スパイの使用は問題ありません。

私は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)の「応答が見つかりません」を解消します。

1
Jamie Dulaney

時々エラーはあなたがばかげたことを逃したためです。私の場合、リクエストクラスとレスポンスクラスで「データクラス」を使用するのに失敗しました。それだけでした。 「relaxedMockks」や「スパイ」などについては何もありません。しかし、メッセージはあなたが持っているのと同じでした:「答えが見つかりません」。

LoginRequestとCustomerResponseがデータクラスであることを確認します。

0
Antonio Martin

まず、テストをデバッグすることをお勧めします。次に、実行に失敗したコードの行を見つけます。私もあなたと同じ経験をしましたが、私の場合、コードが次のように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は、すべての関数に対して単純な値を返すモックです。これにより、各ケースの動作の指定をスキップしながら、必要なものをスタブすることができます。参照型の場合、連鎖モックが返されます。

0
audrians