web-dev-qa-db-fra.com

kotlin mockk pas de réponse erreur trouvée

Salut, j'essaie de me moquer de la réponse que j'obtiens d'un observable unique qui est renvoyé de la modification en utilisant un délégateur que ma classe de présentateur appelle et j'obtiens l'erreur suivante:

io.mockk.MockKException: aucune réponse trouvée pour: LoginPresenter (# 1) .login (LoginRequest ([email protected], password = password123))

Voici mon code de test

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

Mon code Presenter réel ressemble à ceci:

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

je veux essentiellement me moquer des dépendances injectées que vous voyez dans le code principal, puis exécuter un test unitaire de chemin heureux.

Il échoue lorsque j'appelle le presenter.login (loginRequest) avec l'erreur sans réponse trouvée

Ceci est le plugin kotlin extenstion que j'utilise http://mockk.io/

9
Jonathan

Dans votre cas, vous vous êtes moqué des classes testées. Vous avez deux options:

  • se débarrasser de mockk pour loginPresenter, il suffit d'utiliser l'objet d'origine et de définir les propriétés
  • utilisez spyk pour créer un espion. C'est quelque chose entre l'objet d'origine et la maquette

L'exception est throw car les mocks sont stricts par défaut, il ne sait tout simplement pas comment les gérer car les mocks en tant qu'objets ne sont pas du tout initialisés.

En savoir plus sur les simulacres, les espions et les simulacres détendus ici: https://blog.kotlin-academy.com/mocking-is-not-rocket-science-mockk-features-e5d55d735a98

4
oleksiyp

Vous ne devez pas vous moquer de la classe testée. L'utilisation d'un espion est acceptable, cependant, si vous devez vérifier qu'une méthode a été appelée, par exemple.

Je recommanderais pas d'utiliser l'injection dans les classes que vous contrôlez. Les frameworks DI comme Dagger sont parfaits pour les classes que vous ne créez pas, comme les activités et les fragments, mais pour les classes que vous contrôlez, utilisez simplement le constructeur.

class LoginPresenter(private val loginViewCallBack: LoginViewCallBack,
                     private val delegator: DelegatorContract,
                     private val cookieStorage: CookieStore) {

    // rest of your code
}

Maintenant, vous pouvez facilement fournir une maquette, ou une fausse, à votre présentateur de connexion. Vous n'exposez pas non plus les dépendances. Vous pouvez appeler presenter.delegator Depuis votre activité si vous utilisez l'injection, ce que vous ne voulez probablement pas.


Note latérale:

avec votre LoginPresenter en utilisant le constructeur, et en utilisant le poignard, vous créeriez le présentateur comme:

class LoginModule {
    @Provides
    @ActivityScope
    internal providePresenter(loginViewCallBack: LoginViewCallBack,
                              delegator: DelegatorContract,
                              cookieStorage: CookieStore): LoginPresenter = LoginPresenter(loginViewCallBack, delegator, cookieStorage)
}

Si vous souhaitez utiliser l'injection à la place, vous devez simplement vous rappeler de définir les simulations:

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

L'exemple ci-dessus supprimera le "aucune réponse trouvée" pour presenter.login(request) puisque presenter n'est plus un mockk.

1
Jamie Dulaney

Parfois, l'erreur est due au fait que vous avez manqué une chose stupide. Dans mon cas, j'ai manqué d'utiliser des "classes de données" dans mes classes de demande et de réponse. C'était tout. Rien à propos de "relaxMockks" ou "espions" ou autre chose. Mais le message était le même que celui que vous rencontrez: "pas de réponse trouvée".

Vérifiez que votre LoginRequest et CustomerResponse sont des classes de données.

0
Antonio Martin

Tout d'abord, je vous suggère de déboguer votre test. Ensuite, vous trouverez quelle ligne de votre code a échoué. J'ai la même expérience avec vous, mais dans mon cas, mon test a échoué lorsqu'il a atteint le onSuccess, par exemple à partir de votre code:

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

Je pense que votre test échouera après avoir atteint la ligne loginViewCallBack.onLoginSuccess(customerResponse), car loginViewCallback ne se trouve pas dans votre test simulé. Si vous avez une classe d'interface dont vous voulez vous moquer, vous devez l'écrire comme suit:

@RelaxedMockK
lateinit var viewCallback: LoginContract.LoginViewCallBack

dans mon cas, l'erreur not answer found error a été résolue après avoir changé cette interface avec une maquette détendue.

à partir de docs : La maquette détendue est la maquette qui renvoie une valeur simple pour toutes les fonctions. Cela permet d'ignorer la spécification du comportement pour chaque cas, tout en permettant de bloquer les éléments dont vous avez besoin. Pour les types de référence, les maquettes chaînées sont renvoyées.

0
audrians