Kotlin coroutine unit test fails with "Module with the Main dispatcher had failed to initialize"

When running tests e.g for ViewModel that launch coroutines you are most likely to fall into the following exception

java.lang.IllegalStateException: Module with the Main dispatcher had failed to initialize. For tests, Dispatchers.setMain from kotlinx-coroutines-test module can be used

The reason behind this is the lack of Looper.getMainLooper() on the testing environment which is present on a real application. To fix this you need to swap the Main dispatcher with TestCoroutineDispatcher

Make sure you have a coroutine-test dependency on your Gradle file

"org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutine_version"

SOLUTION 1 - Not scalable

Define the following on your test class -> Annotate your class with @ExperimentalCoroutinesApi

val dispatcher = TestCoroutineDispatcher()

@Before
fun setup() {
    Dispatchers.setMain(dispatcher)
}

@After
fun tearDown() {
    Dispatchers.resetMain()
}

Note: You can also pass Dispatchers.Main as constructor dependency for your repositories as CoroutineDispatcher in case you have one. It is recommended not to hardcode your dispatchers on repositories/viewmodels etc WATCH-THIS PLEASEEEEEEEE

Why not scalable: You will need to copy and paste the same code on each test class

SOLUTION 2 - Scalable [Use This - It is used by Google]

In this solution, you create the custom rule. Add a utility class on your test package

@ExperimentalCoroutinesApi
class MainCoroutineRule(
    private val dispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
) : TestWatcher(), TestCoroutineScope by TestCoroutineScope(dispatcher) {
    override fun starting(description: Description?) {
        super.starting(description)
        Dispatchers.setMain(dispatcher)
    }

    override fun finished(description: Description?) {
        super.finished(description)
        cleanupTestCoroutines()
        Dispatchers.resetMain()
    }
}

If you want explanations on the utility class above, refer to this CODE-LAB

On your test class just add this the following lines and you will be good to go

@get:Rule
val coroutineRule = MainCoroutineRule()

I think you can see why this is scalable if you have a lot of test classes

SOLUTION 3 [I hope you don't reach here]

You can also use Dispatchers.Unconfined LINK

A coroutine dispatcher that is not confined to any specific thread. It executes the initial continuation of a coroutine in the current call-frame and lets the coroutine resume in whatever thread that is used by the corresponding suspending function, without mandating any specific threading policy. Nested coroutines launched in this dispatcher form an event-loop to avoid stack overflows

You can add it as follows

@Before
fun setup() {
    Dispatchers.setMain(Dispatchers.Unconfined)
}

@After
fun tearDown() {
    Dispatchers.resetMain()
}

Happy coding . . . .


You don't have access to Dispatchers.Main in unit testing

See https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-test/

Dispatchers.Main Delegation part explains in detail what you need to do.


now you can add this to your test :

Dispatchers.setMain(Dispatchers.Unconfined)

or other dispatcher.. it's experimental but it works!