Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MockK "io.mockk.MockKException: no answer found for:" error

Tags:

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/

like image 660
Jonathan Avatar asked Jan 12 '18 09:01

Jonathan


People also ask

How do you verify in Mockk?

verify supports the same argument matchers as every , along with a few additional matchers. Inside the verification block (between the opening curly bracket { and closing curly bracket } ), you write the method you want to verify.

What is relaxed mock?

Mocking frameworks supports relaxed mocks like mockk's relaxed. This helps in getting a minimal test setup ready for tests without having to provide mock implementations for all methods of the mocked object.


2 Answers

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

like image 68
oleksiyp Avatar answered Sep 26 '22 05:09

oleksiyp


First of all, I suggest you to debug your test. Then you will find which line of your code failed run. I get the same experienced with you, but in my case, my test failed when it reached on onSuccess, for example from your code is:

override fun onSuccess(customerResponse : CustomerResponse) {          loginViewCallBack.onLoginSuccess(customerResponse)          Log.d("JJJ", "login successfully") } 

I think from your test will fail after it hits line loginViewCallBack.onLoginSuccess(customerResponse), because loginViewCallback is not found in your mock test. If you have interface class that you want to mock, you should write it be:

@RelaxedMockK lateinit var viewCallback: LoginContract.LoginViewCallBack 

in my case, error not answer found error resolved after I changed this interface with relaxed mock.

from the docs: Relaxed mock is the mock that returns some simple value for all functions. This allows to skip specifying behavior for each case, while still allow to stub things you need. For reference types chained mocks are returned.

like image 33
audrians Avatar answered Sep 24 '22 05:09

audrians