我在 ViewModel 中为 Compose 使用 MutableStates
var desiredDate: MutableState<Date?> = mutableStateOf(null)
另外我想接收状态的变化inside模型
snapshotFlow { desiredDate.value }
.onEach { updateConfirmationState() }
.launchIn(viewModelScope)
这作为高效代码工作得很好。现在我想测试更新一个状态会导致单元测试中其他状态的更新。我正在使用经典的 MainCoroutineRule 来覆盖 Dispatchers.Main.
现在每个 snapshotFlow 的启动都发生在一个单独的 worker 中,并且由于 MutableState 没有共享,所有的值都是空的。
这样测试应该有方法吧?
刚开始测试我的撰写视图模型并遇到this文章。
snapshotFlow 依赖于快照,当应用新快照时会发出新值,而不仅仅是值更改时。在生产代码中,Compose 管理引擎盖下的所有快照
所以这就是我测试这个视图模型的方式:
class SettingsViewModel @Inject constructor(
userRepository: UserRepository
) : ViewModel() {
private val _user = userRepository.getMe()
private var _selectedAvatar by mutableStateOf<String?>(null)
val state = combine(
_user,
snapshotFlow { _selectedAvatar },
) { user, selectedAvatar ->
SettingsUiState(selectedAvatar ?: user?.avatar)
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(2_000),
initialValue = SettingsUiState()
)
fun updateAvatar(imageUrl: String?) {
_selectedAvatar = imageUrl
}
}
data class SettingsUiState(
val avatar: Any? = null
}
而在实际测试中:
@OptIn(ExperimentalCoroutinesApi::class)
class SettingsViewModelTest {
@get:Rule
val mockkRule = MockKRule(this)
@get:Rule
val dispatcherRule = TestDispatcherRule()
@MockK
lateinit var userRepository: UserRepository
lateinit var viewModel: SettingsViewModel
@Test
fun `state returns updated SettingsUiState when selectedAvatar updates`() = runTest {
val selectedAvatar = "http://www.example.com/2.jpg"
val user = User(1, "John", "http://www.example.com/1.jpg")
every { userRepository.getMe() } returns flowOf(user)
viewModel = SettingsViewModel(userRepository)
viewModel.state.test {
assertEquals(SettingsUiState(user.avatar), awaitItem())
withMutableSnapshot { viewModel.updateAvatar(selectedAvatar) }
assertEquals(SettingsUiState(selectedAvatar), awaitItem())
}
}
}
注意我在
updateAvatar(..)
中调用 .test
,Turbine 有一个关于如何测试共享流的 reference。
此外,如果您的项目中还没有像
TestDispatcherRule
这样的东西,这里有一个sample。