Kotlin Coroutines in Android — Part 12

Testing

Andrea Bresolin
Kin + Carta Created
5 min readApr 5, 2019

--

We’ve been exploring different features of Kotlin coroutines and how to use them in Android. It’s now time to talk about testing.

One thing that we need while testing is using Dispatchers.Unconfined instead of the other dispatchers. The Unconfined dispatcher, as we’ve previously mentioned, makes a coroutine run on the current thread without restricting the execution to a specific thread pool.

Here is the coroutines configuration class with the settings for testing that we’ve previously defined:

Each dispatcher configuration makes use of Dispatchers.Unconfined. This is a BeforeClass method of JUnit that can be used in a base test class.

Another thing that we’re going to need multiple times is a CoroutineScope implementation for testing. Remember that with our use cases for example, we might need to pass the parent CoroutineScope when calling the execution method. So here is an implementation that is suitable for testing:

This is just a standard implementation of CoroutineScope that uses the Unconfined dispatcher for testing.

Remember that each suspend function can only be called within a coroutine or another suspend function. So, how can we call them for testing? We have runBlocking to satisfy this requirement. runBlocking is a coroutine builder that creates a coroutine that runs on the main thread. This is exactly what we need for testing. Unless we’re testing something that has to explicitly be on background threads, most of the times we just want to immediately execute all the coroutines code as if it was on the main thread. Every call becomes then blocking and sequential. Using the Unconfined dispatcher makes sure as well that each new coroutine execution runs on the same thread where the test is currently running, which will typically be the main thread.

Here is an example of a test step that invokes a suspend function:

In this case, subject is a use case whose main method is execute(). This method is a suspend function so it needs to run within a coroutine or another suspend function. We use runBlocking to create a coroutine that runs on the main thread as the other testing code does as well. This allows to keep the coroutine code in sync will all the other testing code. runBlocking is needed only if we have to call suspend functions. There’s no need for it otherwise.

One more tool worth mentioning while talking about testing is CompletableDeferred. If we need to mock a method that returns a Deferred, we typically want to return a Deferred that has already completed with a specific result. This is what CompletableDeferred is for. It creates an already completed Deferred with the specified result. Here is an example:

We mock the result of the executeAsync() method of a use case with Mockito. The method returns a Deferred and for the test we want it to complete with the givenResult value. The use case method also needs the parent coroutine scope as parameter, so we use an instance of TestAppCoroutineScope. anyObj() is just a utility method that allows to use the any() argument matcher from Mockito and return a value that is not null, which is something that is needed multiple times as you’ve probably experienced while using the argument matchers with non-nullable arguments of Kotlin methods. Here is the implementation of anyObj():

These are the main things that we might need while testing code that makes use of coroutines. Anything else is the same as with code that doesn’t use coroutines at all from the testing perspective.

Let’s write a simple test with Mockito for a use case:

This test is for a use case that internally retrieves some data from a remote repository. We test that it returns the fetched data. The use case needs the parent coroutine scope as input of its execution method so we pass an instance of TestAppCoroutineScope. We mock the result returned by the fetch method of the repository and then we verify that the result returned by the use case is the same fetched data. Given that we need to call await() on the Deferred returned by executeAsync() and await() is a suspend function, we use runBlocking to wrap it inside a coroutine that executes on the main thread which is the same thread used by the rest of the testing code.

What’s next

This is the conclusion of this series. I hope it has provided a valuable introduction to Kotlin coroutines for Android apps development. There’s much more to explore about coroutines of course, but this series should have given you a way to start or at least made you curious and willing to learn more about this new powerful development tool. For more examples on coroutines and testing, remember to check the example project and play with it. You might also want to take a look at the kotlinx-coroutines-test module, which is part of the coroutines library, for additional testing utilities.

Get the source code

The source code for this series can be found on GitHub. It’s an example Android project that covers multiple cases. Download it and play with it.

Missed the other parts of this series?

If you’ve missed the other parts of the Kotlin Coroutines in Android series, take a look at the introduction and check the full list of topics.

--

--