5

我有这个视图模型:

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
        }
    }
}

我想编写一个测试来检查状态是否真的LOADINGmyUseCase.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)
    }
}

它失败:

java.lang.AssertionError: 
Expected :LOADING
Actual   :IDLE

我怎样才能解决这个问题?

4

2 回答 2

3

我只需要在测试类中进行一些更改即可使其通过:

@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)
        }
    }
}

视图模型根本不需要更改!:D

感谢 Kiskae 提供如此有用的建议!

于 2019-08-30T18:11:19.023 回答
2

您的问题在于viewModelScope调度到的事实,Dispatcher.MAIN而不是由创建的测试调度程序runBlockingTest。这意味着即使调用advanceTimeBy代码也不会被执行。

Dispatcher.setMain(..)您可以通过将 MAIN 调度程序替换为您的测试调度程序来解决此问题。这将需要您自己管理调度程序,而不是依赖独立的runBlockingTest.

于 2019-08-30T16:40:14.643 回答