Search code examples
kotlinunit-testingkotlin-coroutinescoroutinescope

Coroutine launch not waiting with StandardTestDispatcher


I’ve a ViewModel that has a viewModelScope.launch{} on the init{}. I’m trying to run a test where I set Dispatchers.setMain(StandardTestDispatcher()) but the launch{} is running before I call advanceUntilIdle(). Why is that? Shouldn't it wait?

class TestViewModel : ViewModel() {

    val state = MutableStateFlow("A")

    init {
        viewModelScope.launch {
            state.update { "B" }
        }
    }
}
class TestViewModelTest {

    private lateinit var viewModel: TestViewModel

    @Before
    fun setup() {
        Dispatchers.setMain(StandardTestDispatcher())
        viewModel = TestViewModel()
    }

    @Test
    fun test() = runTest {
        assert(viewModel.state.value == "A") // Fails, value is "B"
        advanceUntilIdle()
        assert(viewModel.state.value == "B")
    }

}

Solution

  • As explained in the issue opened on coroutine's GitHub, runTest automatically runs all enqueued tasks. With this in mind, changing the test to this fix it:

    @Test
    fun test() {
        assert(viewModel.state.value == "A")
        runTest {
            advanceUntilIdle()
            assert(viewModel.state.value == "B")
        }
    }