MockK“io.mockk.MockKException:找不到答案:”错误

jon*_*ney 20 unit-testing kotlin kotlin-android-extensions mockk

嗨,我正在尝试模拟我从使用我的演示者类调用的委托者从改造中返回的单个可观察对象获得的响应,并且出现以下错误:

io.mockk.MockKException:没有找到答案:LoginPresenter(#1).login(LoginRequest(email=hello@gmail.com, 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("hello@gmail.com", "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)
}
Run Code Online (Sandbox Code Playgroud)

我的实际演示者代码如下所示:

@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()
        }
Run Code Online (Sandbox Code Playgroud)

我基本上想模拟您从主代码中看到的注入依赖项,然后运行一个快乐路径单元测试。

当我调用presenter.login(loginRequest) 并没有找到答案错误时,它失败了

这是我使用的 kotlin 扩展插件http://mockk.io/

小智 13

在您的情况下,您嘲笑了正在测试的类。您有两个选择:

  • 摆脱对 loginPresenter 的模拟,只需使用原始对象并设置属性
  • 用于spyk创建间谍。这是原始对象和模拟之间的东西

抛出异常是因为默认情况下模拟是严格的,它只是不知道如何处理它,因为模拟作为对象根本没有初始化。

在此处阅读有关模拟、间谍和轻松模拟的更多信息:https : //blog.kotlin-academy.com/mocking-is-not-rocket-science-mockk-features-e5d55d735a98

  • Mock k 中 InjectMocks 的等价物是什么? (2认同)

aud*_*ans 8

首先,我建议你调试你的测试。然后你会发现你的哪一行代码运行失败。我对你有同样的经历,但就我而言,我的测试在到达时失败onSuccess,例如从你的代码是:

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

我认为从你的测试中它到达 line 后会失败loginViewCallBack.onLoginSuccess(customerResponse),因为loginViewCallback在你的模拟测试中没有找到。如果您有要模拟的接口类,则应将其编写为:

@RelaxedMockK
lateinit var viewCallback: LoginContract.LoginViewCallBack
Run Code Online (Sandbox Code Playgroud)

就我而言,not answer found error在我使用轻松模拟更改此界面后,错误解决了。

来自文档轻松模拟是为所有函数返回一些简单值的模拟。这允许跳过为每种情况指定行为,同时仍然允许存根您需要的东西。对于引用类型,返回链接的模拟。

  • 是的,对我来说也一样。我正在模拟一个接口,并通过使用如下所示的宽松模拟来修复错误: ```mockk&lt;InitialContract&gt;(relaxed = true)``` (2认同)

ano*_*203 8

就我而言,我有一个使用 Kotlin 语法访问 setter 的函数,并且mockk抛出异常,因为您必须定义 setter 行为。这可以通过两种方式完成,具体取决于您是否想要稍后读取该值。

如果你想读取该值,你可以设置一个slot

val myPropertySlot = slot<Int>()
every { myClass.myProperty = capture(myPropertySlot) } just runs
assertThat(myPropertySlot.captured).isEqualTo(1)
Run Code Online (Sandbox Code Playgroud)

如果您只想运行 setter 但不需要该值,则语法会变得更简单:

every { myClass.myProperty = any() } just runs
Run Code Online (Sandbox Code Playgroud)

myProperty如果您尝试在正在测试的代码中像这样访问,这将使您的测试不会抛出:

...
myClass.myProperty = 1
...
Run Code Online (Sandbox Code Playgroud)


Coo*_*ind 7

就我而言,我忘记检查是否调用了一个方法(例如,在我们的代码中object.setData(person.getAge()))。

every { object.setData(any()) } just Runs // Mock method
testedMethod() // Tested code runs here
verify { object.setData(any()) } // Check that object.setData() was called
Run Code Online (Sandbox Code Playgroud)


sur*_*rga 5

如果您正在使用mock并寻找mocking void方法,那么您需要做的就是添加:

returns Unit
Run Code Online (Sandbox Code Playgroud)

或将其更改为以下形式:

doNothing().`when`(mockedFile).write(any())
Run Code Online (Sandbox Code Playgroud)

如https://notwoods.github.io/mockk-guidebook/docs/mockito-migrate/void/中所述