Android + Kotlin + Coroutines + Unit Test

Recently I’ve become an Android Kotlin fan and convert. Then I read about coroutines and thought all my geeky Christmas’ had come at once.

Coroutines

Coroutines in Kotlin enable easy asynchronous tasks. With a few lines we can make asynchronous calls and get their response

-- PRESENTER
fun getListOfThings() {
launch(Android) {
asyncTask { storageHelper.getListFromDb() }.await()
.let { list ->
view.useTheResult(list)
}
}
}

fun <T> asyncTask(function: () -> T): Deferred<T> {
return async(CommonPool) { function() }
}

A couple of notes around this code:

  • Android class is Miguel Castiblanco’s code directly from this excellent post
  • asyncTask is a helper function I have written to reduce code, and make things a little neater.

Until recently I’ve been using RxJava fromCallable for simple asynchronous tasks, now I can remove that dependency. So all is good, well, yes… but now like the good developers we are we need to unit test…

Unit Test

Now we want to unit test the getListOfThings method. First we have the mocking issue around Kotlin classes and methods being final by default. Not too bad (either open them, or use the latest Mockito features for mocking final).

In my previous async implementation (RxJava) you can quite easily swap out schedulers for test, making asynchronous into synchronous for test purposes. This proved impossible using coroutines.

Instead, I had to take a different approach… first lets write a unit test for getListOfThings:

-- TEST
@Test
fun getPackageFiltersForWidget() {
val list: ArrayList<PackageFilter> = ArrayList()
`when`(storageHelper.getListFromDb()).thenReturn(list)
presenter.getListOfThings()
Mockito.verify(view).useTheResult(ArgumentMatchers.eq(list))
}
-- PRESENTER
fun getListOfThings() {
launch(Android) {
asyncTask { storageHelper.getListFromDb() }.await()
.let { list ->
view.useTheResult(list)
}
}
}

fun <T> asyncTask(function: () -> T): Deferred<T> {
return async(CommonPool) { function() }
}
-- ACTIVITY
fun getList() {
presenter.getListOfThings()
}

This will fail. It will complain about the Looper class not being mocked, as it’s trying to use the Android CoroutineContext.

What we need to do is move the launch(Android) section out of the presenter, so we can test the logic only. But we don’t want the presenter method being called on the UI Thread.

Enter the suspend keyword and runBlocking:

-- TEST
@Test
fun getPackageFiltersForWidget() {
val list: ArrayList<PackageFilter> = ArrayList()
`when`(storageHelper.getListFromDb()).thenReturn(list)
runBlocking {
presenter.getListOfThings()
}
Mockito.verify(view).useTheResult(ArgumentMatchers.eq(list))
}
-- PRESENTER
suspend fun getListOfThings() {
asyncTask { storageHelper.getListFromDb() }.await()
.let { list ->
view.useTheResult(list)
}
}

fun <T> asyncTask(function: () -> T): Deferred<T> {
return async(CommonPool) { function() }
}
-- ACTIVITY
fun getList() {
launch(Android) {
presenter.getListOfThings()
}
}

OK. If you try to call the getListOfThings method you’ll get a compile error stating that a suspend function should be called from a coroutine. So we have safety.

You get the same error inside the test, so surround the call to the getListOfThings method with runBlocking.

If we run the test now, all green .. yay!

Kotlin Setup

Coroutines are only available in Kotlin 1.1, which (at time of writing) is in RC.

You will need to setup your project to use 1.1, and then add the following import

compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.11-rc"