如何用Mockk测试Observable的订阅调用?ViewModel测试遇断言异常
问题分析与解决
核心问题
- RxJava线程异步执行:测试里
Schedulers.io()和AndroidSchedulers.mainThread()是异步调度的,verify会在订阅回调触发前执行,导致断言失败。 - init块调用时机冲突:
subscribeToTablesUpdates()在ViewModel的init块中执行,测试代码里的mock设置是在ViewModel实例化之后,此时函数已经调用过,mock完全没生效。
解决方案
1. 让RxJava在测试中同步执行
在测试类的前后置方法里替换RxJava的调度器,强制所有操作在当前线程同步完成:
@Before fun setup() { RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() } RxAndroidPlugins.setMainThreadSchedulerHandler { Schedulers.trampoline() } } @After fun teardown() { RxJavaPlugins.reset() RxAndroidPlugins.reset() }
这样订阅回调会立刻执行,verify就能捕捉到updateTablesState的调用。
2. 调整mock与ViewModel初始化顺序
因为函数在init块里,必须先把mock配置好,再创建ViewModel实例:
private lateinit var viewModel: YourViewModel private val mockDataManager = mockk<DataManager>() @Before fun setup() { // 先配置RxJava同步调度 RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() } RxAndroidPlugins.setMainThreadSchedulerHandler { Schedulers.trampoline() } // 先mock dataManager的返回值 val mockedTablesList = mockk<List<Table>>() every { mockDataManager.getTablesList() } returns Observable.just(mockedTablesList) // 最后初始化ViewModel viewModel = YourViewModel(mockDataManager) } @Test fun subscribeToTablesListTest() { val mockedTablesList = mockk<List<Table>>() // 不用手动调用subscribeToTablesUpdates,init块已经执行过了 verify { viewModel.updateTablesState(mockedTablesList) } }
注意:ViewModel的构造函数要接收dataManager参数,方便测试时传入mock实例。
3. 修正测试代码语法错误
原测试里mockk<List<Table>>()少了闭合尖括号,正确写法是mockk<List<Table>>()。
完整测试示例
class YourViewModelTest { private lateinit var viewModel: YourViewModel private val mockDataManager = mockk<DataManager>() @Before fun setup() { // 配置RxJava同步调度 RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() } RxAndroidPlugins.setMainThreadSchedulerHandler { Schedulers.trampoline() } // Mock数据返回 val mockedTablesList = mockk<List<Table>>() every { mockDataManager.getTablesList() } returns Observable.just(mockedTablesList) // 初始化ViewModel viewModel = YourViewModel(mockDataManager) } @After fun teardown() { // 重置RxJava配置,避免影响其他测试 RxJavaPlugins.reset() RxAndroidPlugins.reset() } @Test fun subscribeToTablesListTest() { val mockedTablesList = mockk<List<Table>>() verify { viewModel.updateTablesState(mockedTablesList) } } }
内容的提问来源于stack exchange,提问作者Nikita Vasko
相关产品推荐
相关产品推荐

