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/
Dans votre cas, vous vous êtes moqué des classes testées. Vous avez deux options:
spyk
pour créer un espion. C'est quelque chose entre l'objet d'origine et la maquetteL'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
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.
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.
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.