从单元测试的 Dispatcher 访问 ViewModel MutableState?

问题描述 投票:0回答:1

我在 ViewModel 中为 Compose 使用 MutableStates

var desiredDate: MutableState<Date?> = mutableStateOf(null)

另外我想接收状态的变化inside模型

    snapshotFlow { desiredDate.value }
            .onEach { updateConfirmationState() }
            .launchIn(viewModelScope)

这作为高效代码工作得很好。现在我想测试更新一个状态会导致单元测试中其他状态的更新。我正在使用经典的 MainCoroutineRule 来覆盖 Dispatchers.Main.

现在每个 snapshotFlow 的启动都发生在一个单独的 worker 中,并且由于 MutableState 没有共享,所有的值都是空的。

这样测试应该有方法吧?

android kotlin kotlin-coroutines mutable
1个回答
0
投票

刚开始测试我的撰写视图模型并遇到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

© www.soinside.com 2019 - 2024. All rights reserved.