首页 > 解决方案 > 使用 MockK 测试 LiveData 和协程

问题描述

我有这个视图模型:

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

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

标签: androidtestingandroid-livedatakotlin-coroutinesmockk

解决方案


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

@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 提供如此有用的建议!


推荐阅读