我正在尝试为我的 ViewModel 编写单元测试,但我不知道如何处理 LiveData 函数。
具体来说,我无法验证接收 LiveData Observer
的所有值。
关于我有一个发出值然后传递为 LiveData 的流用例,测试 操作
功能的最佳方法是什么?
在下面的代码中,您可以发现我只能读取值“endLoading”
,但我想检查所有值:“startLoading”
, “Hello Dummy $input”
, “endLoading”
MainViewModel.kt
class MainViewModel(val useCase: DummyUseCase = DummyUseCase()): ViewModel() {
fun operation(value: Int): LiveData<String> = useCase.invoke(value)
.transform { response ->
emit(response)
}.onStart {
emit("startLoading")
}.catch {
emit("ERROR")
}.onCompletion {
emit("endLoading")
}.asLiveData(viewModelScope.coroutineContext)
}
MainViewModelTest.kt
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.Observer
import io.mockk.MockKAnnotations
import io.mockk.coEvery
import io.mockk.impl.annotations.MockK
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@ExperimentalCoroutinesApi
class MainViewModelTest {
//region Setup
@get:Rule
val rule = InstantTaskExecutorRule()
private val testDispatcher = TestCoroutineDispatcher()
@MockK private lateinit var stateObserver: Observer<String>
@MockK private lateinit var useCase: DummyUseCase
private lateinit var viewModel: MainViewModel
@Before
fun setup() {
MockKAnnotations.init(this, relaxUnitFun = true)
Dispatchers.setMain(testDispatcher)
viewModel = MainViewModel(useCase)
}
@After
fun teardown() {
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
//endregion
@Test // AAA testing
fun `when my flow succeeds, return a state String`() {
runBlocking {
//Arrange
val input = 10
coEvery { useCase.invoke(input) }.returns(flow {
emit("Hello Dummy $input")
})
//Act
val actual = viewModel.operation(input).apply {
observeForever(stateObserver)
}
//Assert
// I want to assert here every value received in the observer of the "actual" LiveData
// How? :(
assertNotNull(actual.value) // is always "endLoading"
}
}
}
最佳答案
您可以使用自定义 Observer<T>
测试 LiveData执行。创建一个观察者,记录所有发出的值并让您针对历史进行断言。
记录值的观察者可能如下所示:
class TestableObserver<T> : Observer<T> {
private val history: MutableList<T> = mutableListOf()
override fun onChanged(value: T) {
history.add(value)
}
fun assertAllEmitted(values: List<T>) {
assertEquals(values.count(), history.count())
history.forEachIndexed { index, t ->
assertEquals(values[index], t)
}
}
}
您可以使用 assertAllEmitted(...)
断言 LiveData 是否发出所有给定值。功能。
测试函数将使用 TestableObserver
的实例类而不是模拟类:
@Test // AAA testing
fun `when my flow succeeds, return a state String`() {
runBlocking {
//Arrange
val stateObserver = TestableObserver<String>()
val input = 10
coEvery { useCase.invoke(input) }.returns(flow {
emit("Hello Dummy $input")
})
//Act
val actual = viewModel.operation(input).apply {
observeForever(stateObserver)
}
//Assert
stateObserver.assertAllEmitted(
listOf(
"startLoading",
"Hello Dummy 10",
"endLoading"
)
)
}
}
使用模拟框架和断言框架可以断言 LiveData 的历史,但是,我认为实现这个可测试的观察者更具可读性。
关于kotlin - 安卓 : unit testing of LiveData and Flow,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67169360/