Android Compose中如何测试MutableStateFlow?
问题描述
ViewModel 代码
private val _prefix = MutableStateFlow("") val prefix: StateFlow<String> = _prefix private val _firstName = MutableStateFlow("") val firstName: StateFlow<String> = _firstName private val _middleName = MutableStateFlow("") val middleName: StateFlow<String> = _middleName private val _lastName = MutableStateFlow("") val lastName: StateFlow<String> = _lastName private val _suffix = MutableStateFlow("") val suffix: StateFlow<String> = _suffix fun saveContact( prefix: String, firstName: String, middleName: String, lastName: String, suffix: String, ) { _prefix.value = prefix _firstName.value = firstName _middleName.value = middleName _lastName.value = lastName _suffix.value = suffix }
所有字段输入完成后,视图会调用saveContact方法。
测试代码
@Test fun testView_success() { lateinit var viewModel: ViewModel ctr.setContent { viewModel = ViewModel(AppDatabase.getDatabase(LocalContext.current).dao) View( viewModel = viewModel, buttonActions = ViewButtonActions( onNavigateBack = {}, onSave = {}, ) ) } // 确认显示正确视图 ctr.onNodeWithTag(tags.testTag).assertIsDisplayed() /* * 个人信息 */ // 显示所有字段 ctr.onNodeWithTag(tags.buttonShowHiddenContentPD).performClick() // 输入文本 ctr.onNodeWithTag(tags.textFieldPrefix).performTextInput(TestData.prefix) ctr.onNodeWithTag(tags.textFieldFirstName).performTextInput(TestData.firstName) ctr.onNodeWithTag(tags.textFieldMiddleName).performTextInput(TestData.middleName) ctr.onNodeWithTag(tags.textFieldLastName).performTextInput(TestData.lastName) ctr.onNodeWithTag(tags.textFieldSuffix).performTextInput(TestData.suffix) // 点击保存按钮 ctr.onNodeWithTag(tags.buttonSave) // 检查文本是否传递成功 assertEquals( TestData.prefix, // 值为"Mr" viewModel.prefix.value ) assertEquals( TestData.firstName, // "Joe" viewModel.firstName.value ) assertEquals( TestData.middleName, // "Ray" viewModel.middleName.value ) assertEquals( TestData.lastName, // "Bloggs" viewModel.lastName.value ) assertEquals( TestData.suffix, // "B.sc" viewModel.suffix.value ) }
运行错误
junit.framework.ComparisonFailure: expected:<[Mr]> but was:<[]> at junit.framework.Assert.assertEquals(Assert.java:85) at junit.framework.Assert.assertEquals(Assert.java:91)
测试时StateFlow的数据未更新,如何正确测试MutableStateFlow/StateFlow?
解决方案
1. 修复保存按钮点击逻辑
测试代码中仅获取了保存按钮节点,未执行点击操作,这是核心问题之一:
// 错误写法 ctr.onNodeWithTag(tags.buttonSave) // 正确写法 ctr.onNodeWithTag(tags.buttonSave).performClick()
2. 等待StateFlow数据更新
StateFlow的更新是异步的,直接读取value可能拿到旧值,可通过两种方式处理:
方式一:使用协程测试框架runTest
依赖kotlinx-coroutines-test库,在测试函数中使用runTest挂起协程,通过first()获取StateFlow的最新值:
@Test fun testView_success() = runTest { lateinit var viewModel: ViewModel ctr.setContent { viewModel = ViewModel(AppDatabase.getDatabase(LocalContext.current).dao) View( viewModel = viewModel, buttonActions = ViewButtonActions( onNavigateBack = {}, // 确保回调正确调用saveContact并传入输入字段值 onSave = { viewModel.saveContact( prefix = /* 对应输入框的值 */, firstName = /* 对应输入框的值 */, middleName = /* 对应输入框的值 */, lastName = /* 对应输入框的值 */, suffix = /* 对应输入框的值 */ ) } ) ) } // 执行前置操作:显示字段、输入文本 ctr.onNodeWithTag(tags.buttonShowHiddenContentPD).performClick() ctr.onNodeWithTag(tags.textFieldPrefix).performTextInput(TestData.prefix) ctr.onNodeWithTag(tags.textFieldFirstName).performTextInput(TestData.firstName) ctr.onNodeWithTag(tags.textFieldMiddleName).performTextInput(TestData.middleName) ctr.onNodeWithTag(tags.textFieldLastName).performTextInput(TestData.lastName) ctr.onNodeWithTag(tags.textFieldSuffix).performTextInput(TestData.suffix) // 点击保存按钮 ctr.onNodeWithTag(tags.buttonSave).performClick() // 收集最新值并断言 assertEquals(TestData.prefix, viewModel.prefix.first()) assertEquals(TestData.firstName, viewModel.firstName.first()) assertEquals(TestData.middleName, viewModel.middleName.first()) assertEquals(TestData.lastName, viewModel.lastName.first()) assertEquals(TestData.suffix, viewModel.suffix.first()) }
first()会挂起直到收到StateFlow的最新发射值。
方式二:使用UI测试等待机制
通过Espresso的waitUntil方法等待StateFlow值更新:
// 等待prefix值更新为预期值 ctr.waitUntil { viewModel.prefix.value == TestData.prefix } // 其他字段同理可添加等待逻辑 // 执行断言 assertEquals(TestData.prefix, viewModel.prefix.value) assertEquals(TestData.firstName, viewModel.firstName.value)
3. 确认View的回调逻辑正确
检查View实现,确保点击保存按钮时,onSave回调会将输入框的实际值正确传递给viewModel.saveContact方法,若传值逻辑缺失,ViewModel的StateFlow不会更新。
内容的提问来源于stack exchange,提问作者JamieRhys
相关产品推荐
相关产品推荐

