You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MockK报no answer found错误,Retrofit Single模拟单元测试失败求助

解决MockK中no answer found的错误

这个错误的核心原因很明确:你错误地mock了要测试的LoginPresenter本身,而不是使用真实的Presenter实例并mock它的依赖项。MockK的mock对象默认不会执行真实的方法逻辑,当你调用presenter.login(loginRequest)时,MockK找不到这个方法的预设响应,就会抛出no answer found异常。

接下来一步步修正你的测试:

1. 使用真实的Presenter实例,而非mock它

我们要测试的是LoginPresenter的真实业务逻辑,所以应该创建它的真实实例,然后手动注入我们mock好的依赖(单元测试里不需要依赖注入框架,直接赋值即可)。

2. 修正依赖mock的匹配与行为

  • 先修正Presenter代码里的笔误:saveCookieAndContinue方法中调用了accountDelegator.getCustomer(),但你的Presenter注入的属性是delegator,这里需要改成delegator.getCustomer(),否则测试会因为找不到accountDelegator而报错。
  • 确保delegator.login()的参数匹配准确,或者用any()来匹配任意LoginRequest(如果不需要严格匹配参数的话)。
  • 处理RxJava线程问题:单元测试中不能使用Android主线程调度器,需要通过RxJavaPlugins替换成同步调度器,避免测试卡住或报错。

修正后的完整测试代码

import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import io.reactivex.Single
import io.reactivex.plugins.RxJavaPlugins
import io.reactivex.schedulers.Schedulers
import org.junit.Before
import org.junit.Test
import org.junit.After

class LoginPresenterTest {
    private lateinit var presenter: LoginPresenter
    private val delegator = mockk<AccountDelegatorContract>()
    private val viewCallback = mockk<LoginContract.LoginViewCallBack>(relaxed = true) // relaxed=true避免未预设方法报错
    private val cookieStore = mockk<PianoCookieStore>(relaxed = true)
    private val loginRequest = LoginRequest("hello@gmail.com", "password123")
    private val customerResponse = CustomerResponse("jon", "richy")

    @Before
    fun setup() {
        // 替换RxJava调度器为同步的trampoline,解决Android主线程依赖问题
        RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
        RxJavaPlugins.setMainThreadSchedulerHandler { Schedulers.trampoline() }

        // 创建真实Presenter实例,手动注入mock依赖
        presenter = LoginPresenter().apply {
            loginViewCallBack = viewCallback
            delegator = this@LoginPresenterTest.delegator
            cookieStorage = cookieStore
        }

        // 预设依赖的行为
        every { delegator.login(any()) } returns Single.just(Response.success<Void>(null))
        every { delegator.getCustomer() } returns Single.just(customerResponse)
    }

    @Test
    fun testLoginSuccessFlow() {
        // 执行测试方法
        presenter.login(loginRequest)

        // 验证流程是否正确执行
        verify { delegator.login(loginRequest) }
        verify { cookieStore.saveSessionCookies(any()) }
        verify { delegator.getCustomer() }
        verify { viewCallback.onLoginSuccess(customerResponse) }
    }

    @After
    fun tearDown() {
        // 恢复RxJava默认调度器,避免影响其他测试
        RxJavaPlugins.reset()
    }
}

关键说明

  • relaxed = true:给viewCallback和cookieStore加上这个参数,意味着它们的未预设方法会返回默认值(比如null、空列表等),避免因为一些无关方法调用而抛出异常。
  • RxJava调度器替换:必须在测试前替换,否则AndroidSchedulers.mainThread()会在单元测试环境中抛出异常,因为没有Android主线程上下文。
  • 验证逻辑:用verify来确认依赖方法是否被正确调用,这是单元测试的核心,确保Presenter的流程符合预期。

额外注意点

如果你的Presenter构造函数是@Inject constructor(),请确保它是public的,否则无法在测试中直接实例化。如果是private的,建议调整构造函数可见性,或者用反射创建实例,但优先选择测试友好的构造方式。

内容的提问来源于stack exchange,提问作者Jono

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:01:58