On testing — Kotlin Coroutines
Or how to pretend you know what you are doing on pull requests
This is part of a series head-scratching my way into coroutines. It can be read as a standalone although you are missing out on some stale memes on the other one:

Where we left off
The little repository class fetching a Reddit post works fine (?!) and your pull request is ready.
class RedditRepository(private val dispatcherProvider: DispatcherProvider) {
suspend fun getPost(): State.Post {
return withContext(dispatcherProvider.io) {
// ...imaginary operation that will take a while
// ..
State.Post("I hope this post gets me karma points") // return on completion
}
}
}RedditRepository.kt
Hol’ up ❗
This surely needs some tests, right?
Get some dependencies first
dependencies {
// testing dependencies
testImplementation 'androidx.test.ext:junit:1.1.2-alpha03'
testImplementation 'org.mockito:mockito-core:3.0.0'
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0'
testImplementation 'org.mockito:mockito-inline:3.0.0'
testImplementation 'org.amshove.kluent:kluent:1.51'
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.2'
testImplementation 'androidx.arch.core:core-testing:2.1.0'
}build.gradle
Enter the CoroutineTestRule
We need some kind of test rule class that will do all the work for us on every test so you don’t have to copy-paste the same code everywhere.
@ExperimentalCoroutinesApi
class CoroutineTestRule(
val testDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()
) : TestWatcher() {
val testDispatcherProvider = object : DispatcherProvider {
override val io: CoroutineDispatcher = testDispatcher
override val ui: CoroutineDispatcher = testDispatcher
override val default: CoroutineDispatcher = testDispatcher
override val unconfined: CoroutineDispatcher = testDispatcher
}
override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(testDispatcher)
}
override fun finished(description: Description?) {
super.finished(description)
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
}CoroutineTestRule.kt
Remember, DispatcherProvider is our own little interface:
interface DispatcherProvider {
val io: CoroutineDispatcher
val ui: CoroutineDispatcher
val default: CoroutineDispatcher
val unconfined: CoroutineDispatcher
}DispatcherProvider.kt
This test rule is going to take care of running our tests on the TestCoroutineDispatcher, cleaning up after they are done so there’s no memory leaks messing with your YouTube watching.
A few more words on the other stuff:
- @ExperimentalApi is there to stop editor warning messages. (truly cutting edge work going on here)
- TestCoroutineDispatcher is included in the kotlinx.coroutines.test library. It’s exactly what it says on the tin and provides some additional functionality to make testing easier.
- Since we are using our own DispatcherProvider we need a way to provide the TestCoroutineDispatcher into any class that we want to test. The testDispatcherProvider takes care of that.
Setting up
Let’s get a test with this rule inside.
@ExperimentalCoroutinesApi
class RedditRepositoryTest {
@get:Rule
val coroutineTestRule = CoroutineTestRule()
private lateinit var redditRepository: RedditRepository
@Before
fun setUp() {
redditRepository = RedditRepository(coroutineTestRule.testDispatcherProvider)
}
}RedditRepositoryTest.kt
Note that the dispatcher for RedditRepository is provided by the coroutineTestRule.
Any other dependencies that RedditRepository might want can easily be mocked but that’s not the point here.
Am I testing yet?
@Test
fun `getPost actually returns the post I am expecting`() {
// run the test on the testDispatcher provided by the coroutineTestRule
coroutineTestRule.testDispatcher.runBlockingTest {
// Given
val expectedPost: State.Post = State.Post("I hope this post gets me karma points")
// When
val actualPost: State.Post = redditRepository.getPost()
// Then
assertEquals(expectedPost, actualPost)
}
}Test.kt
You can even avoid calling coroutinesTestRule.testDispatcher.runBlockingTest by creating an extension function:
@ExperimentalCoroutinesApi
fun CoroutineTestRule.runBlockingTest(block: suspend TestCoroutineScope.() -> Unit) {
testDispatcher.runBlockingTest(block)
}ext.kt
The TestCoroutineDispatcher also has a few cool methods like advanceTimeBy(millis: Long) in case you are testing time sensitive code.
The end?
Next part we’ll wrap things up testing the ViewModel and the LiveData associated with it to get a complete picture.

Later.