Unit testing kotlin coroutines on Android with JUnit 5

JUnit 5 extensions vs JUnit 4 rules

Roman Tikonov
2 min readMar 8, 2020
Photo by Franck V. on Unsplash

If you unit test coroutines in your Android app, you have already seen this exception:

Exception in thread "main @coroutine#1" java.lang.IllegalStateException: Module with the Main dispatcher had failed to initialize. For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used

For those of you who use or plan to use JUnit 5 in your projects, I’ll show how to write a JUnit 5 test extension that fixes this problem.

JUnit 4

We know what caused the exception above: Dispatchers.Main is not available in unit tests because it relies on Looper.getMainLooper() from Android SDK. As the hint in the message says, you can fix it by mocking Dispatchers.Main , and there are articles out there explaining how to do it with JUnit 4. For example, here is a solution for JUnit 4 that invokes Dispatchers.setMain() and Dispatchers.resetMain() before/after each test:

JUnit 4 rule that replaces Dispatchers.Main with TestCoroutineDispatcher

JUnit 5

If you are using JUnit 5 / Jupiter in your project, the solution above won’t work. A concept of rules was deprecated in JUnit 5 in favour of a new concept of test extensions. Let’s convert the rule above to a JUnit 5 extension:

JUnit 5 extension that replaces Dispatchers.Main with TestCoroutineDispatcher

JUnit 5 beforeEach(ExtensionContext) and afterEach(ExtensionContext) are equivalent to JUnit 4 starting(Description) and finished(Description) : they are called before and after each test method annotated with @Test . The extension above will mock Dispatchers.Main before each test and reset the mock afterwards.

And now let’s add this extension to a test class:

Example unit test that uses CoroutinesTestExtension

More about JUnit 5 extensions here: https://junit.org/junit5/docs/current/user-guide/#extensions

Bonus: Power of time machine

Notice that CoroutinesTestExtension from above implements TestCoroutineScope interface (by delegating it to a TestCoroutineScopeImpl that delegates it to the same TestCoroutineDispatcher instance that replaces Dispatchers.Main). It means that you can control the execution of coroutines on Dispatchers.Main by calling


Rx fans will notice similarities to the API of RxTestScheduler.

Note that this does not affect coroutines running on other dispatchers, e.g. Dispatchers.IO . If your class under test uses other dispatchers, consider passing them as a constructor argument and mocking them with a TestCoroutineDispatcher in unit tests.


If you want to migrate your unit tests from JUnit 4 to JUnit 5, replace the JUnit 4 dependency with

testImplementation "org.junit.jupiter:junit-jupiter:$junit_jupiter_version"

and replace your JUnit 4 imports with JUnit 5, e.g.:

// replace
// with
import org.junit.jupiter.api.Test

Happy coding!