Mil*_*k27 5 testing android android-livedata mockk kotlin-coroutines
我有这个视图模型:
class MyViewModel(private val myUseCase: MyUseCase) : ViewModel() {
val stateLiveData = MutableLiveData(State.IDLE)
fun onButtonPressed() {
viewModelScope.launch {
stateLiveData.value = State.LOADING
myUseCase.loadStuff() // Suspend
stateLiveData.value = State.SUCCESS
}
}
}
Run Code Online (Sandbox Code Playgroud)
我想编写一个测试来检查状态是否真的LOADING在myUseCase.loadStuff()运行时。我正在使用 MockK。这是测试类:
@ExperimentalCoroutinesApi
class MyViewModelTest {
@get:Rule
val rule = InstantTaskExecutorRule()
private lateinit var myUseCase: MyUseCase
private lateinit var myViewModel: MyViewModel
@Before
fun setup() {
myUseCase = mockkClass(MyUseCase::class)
myViewModel = MyViewModel(myUseCase)
}
@Test
fun `button click should put screen into loading state`() = runBlockingTest {
coEvery { myUseCase.loadStuff() } coAnswers { delay(2000) }
myViewModel.onButtonPressed()
advanceTimeBy(1000)
val state = myViewModel.stateLiveData.value
assertEquals(State.LOADING, state)
}
}
Run Code Online (Sandbox Code Playgroud)
它失败:
java.lang.AssertionError:
Expected :LOADING
Actual :IDLE
Run Code Online (Sandbox Code Playgroud)
我怎样才能解决这个问题?
我只需要在测试类中进行一些更改即可使其通过:
@ExperimentalCoroutinesApi
class MyViewModelTest {
@get:Rule
val rule = InstantTaskExecutorRule()
private val dispatcher = TestCoroutineDispatcher()
private lateinit var myUseCase: MyUseCase
private lateinit var myViewModel: MyViewModel
@Before
fun setup() {
Dispatchers.setMain(dispatcher)
myUseCase = mockkClass(MyUseCase::class)
myViewModel = MyViewModel(myUseCase)
}
@After
fun cleanup() {
Dispatchers.resetMain()
}
@Test
fun `button click should put screen into loading state`() {
dispatcher.runBlockingTest {
coEvery { myUseCase.loadStuff() } coAnswers { delay(2000) }
myViewModel.onButtonPressed()
// This isn't even needed.
advanceTimeBy(1000)
val state = myViewModel.stateLiveData.value
assertEquals(State.LOADING, state)
}
}
}
Run Code Online (Sandbox Code Playgroud)
视图模型根本不需要更改!:D
感谢 Kiskae 提供如此有用的建议!
您的问题在于viewModelScope调度到Dispatcher.MAIN,而不是由创建的测试调度程序runBlockingTest。这意味着即使调用advanceTimeBy该代码也不会被执行。
Dispatcher.setMain(..)您可以通过使用测试调度程序替换 MAIN 调度程序来解决该问题。这将需要您自己管理调度程序,而不是依赖独立的runBlockingTest.