kotlin mockk not answer found error

Jonathan picture Jonathan · Jan 12, 2018 · Viewed 32.7k times · Source

Hi i am trying to mock the response i get from a Single observable that gets returned from retrofit using a delegator that my presenter class calls and i am getting the following error:

io.mockk.MockKException: no answer found for: LoginPresenter(#1).login(LoginRequest([email protected], password=password123))

Here is my test code

 @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)
    }

My actual Presenter code looks like this:

 @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()
    }

i basically want to mock the injected dependencies you see from the main code and then run a happy path unit test.

It fails when i call the presenter.login(loginRequest) with the no answer found error

This is the kotlin extenstion plugin i am using http://mockk.io/

Answer

oleksiyp picture oleksiyp · Jan 25, 2018

In your case you mocked the classes being tested. You have two options:

  • get rid of mockk for loginPresenter, just use original object and set properties
  • use spyk to create spy. This is something in between original object and mock

The exception is throw because mocks are strict by default, it just do not know how to handle it because mocks as objects are not initialized at all.

Read more about mocks, spies and relaxed mocks here: https://blog.kotlin-academy.com/mocking-is-not-rocket-science-mockk-features-e5d55d735a98