Kotlin Everywhere. Coroutines, Tests, Robots and much more…

Ashwini Kumar
ProAndroidDev
Published in
6 min readAug 2, 2019

--

Prologue

The year is 2019 and with every passing month, technology and the stack underneath are upgrading. For eg: The Android permissions which were open for all, became dangerous, then restricted and then permissible only when required. Things are changing fast for the better and more solid Android development with core principles getting intact.

Change is the only constant — Mahatma Gandhi

Two years back when TvMaze repository was created, the aim was to follow the latest tech stack and architecture. So the app was first designed in MVP pattern and then last year migrated to MVVM by Android Architecture Components powered by RxJava and Dagger 2. Now, the complete TvMaze repository has been rewritten in Kotlin with the latest tech stack and language goodness. So let’s dive in.

Upgrade to Moshi

TvMaze earlier relied upon Gson for converting JSON to POJO and back. To promote immutability, AutoValue was being used with Parcelable and Gson extensions by Ryan Harter. Gson TypeAdapters helped in avoiding reflection while making the conversion. AutoValue also helped in creating builders while still maintaining immutability. A typical AutoValue class will look like:

Too much of a boilerplate isn’t it?! Also, since the libraries are moving to Kotlin first then why not upgrade for a better future with the goodness of the language. Moshi solves all the above to stand out as better JSON serialization library. It provides first-hand Kotlin support with moshi-kotlin and codegen . Kotlin data classes are immutable and copy method helps in creating the builder. Kotlin’s@Parcelize helps to replace the AutoValue parcel extension and thus removing the complete AutoValue library. So the above data class becomes pretty neat and small.

@Parcelize
@JsonClass(generateAdapter = true)
data class Episode(
val id: Long,
val url: String?) : Parcelable

Migration to Coroutine from RxJava — A Paradigm Shift

Coroutines are new ways to encourage asynchronous programming. They help in writing asynchronous operations in a synchronous manner. There are tons of articles which already explains the Whys and Hows of Coroutines. So let’s jump directly into implementation.

With Retrofit latest release, Coroutine is officially supported. That means, you can remove retrofit’s RxJava2CallAdapterFactory . Just make all the retrofit calls to suspend functions and return the values directly.

interface TvMazeApi {
@GET("/schedule")
suspend fun getCurrentSchedule(
@Query("country") country: String,
@Query("date") date: String
): List<Episode>

@GET("/shows")
suspend fun getShows(@Query("page") pageNumber: Int): List<Show>
}

The home screen of TvMaze shows the list of shows fetched from the TVDB API clubbing them with user’s favorites. A user can mark any show as favorite which gets stored via Room. Room library now comes with Coroutine support that means all the Dao operations can be made suspending thus removing RxJava completely. So our ShowDao becomes:

Now we are ready to load data for Home screen. HomeViewModel previously was relying on RxJava heavily. When the onScreenCreated was called, two streams: one from API and other from Db were zipped using Single.zipto give the combined result.

public void onScreenCreated() {
isLoading.setValue(true);
String currentDate = getCurrentDate();

Single<List<Show>> favoriteShows = favoriteShowsRepository
.getAllFavoriteShows()
.map(this::convertToShows);

Single<List<Episode>> episodes = tvMazeApi.getCurrentSchedule(COUNTRY_US, currentDate);
Single<List<Episode>> zippedSingleSource =
Single.zip(episodes, favoriteShows, this::favorites);
Disposable homeDisposable = zippedSingleSource.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(this::onSuccess, this::onError);
compositeDisposable.add(homeDisposable);
}

Let's convert the above function to coroutines. Since Retrofit and Room are already on Coroutine support, they respect the suspend calls. That means, viewModelScope will launch a new coroutine, which then calls retrofit’s suspend function tvMazeApi.getCurrentSchedule. The retrofit will move the network operation to IO-bound coroutine via Dispatchers.IO and once the suspending function will complete, it will return the result back on Dispatchers.Main. A similar case will happen with Room suspend calls.

fun onScreenCreated() {
homeViewStateLiveData.value = Loading
val coroutineExceptionHandler = CoroutineExceptionHandler { _, exception ->
onError(exception)
}
// viewModelScope launches the new coroutine on Main Dispatcher internally
viewModelScope
.launch(coroutineExceptionHandler) {
// Get favorite shows from db, suspend function in room will launch a new coroutine with IO dispatcher
val
favoriteShowIds = favoriteShowsRepository.allFavoriteShowIds()
// Get shows from network, suspend function in retrofit will launch a new coroutine with IO dispatcher
val
episodes = tvMazeApi.getCurrentSchedule(COUNTRY_US, currentDate)

// Return the result on main thread via Dispatchers.Main
homeViewStateLiveData.value = Success(HomeViewData(getShowsWithFavorites(episodes, favoriteShowIds)))
}
}

viewModelScope is a lifecycle Kotlin extension to support coroutines. This handles the cancellation of coroutine when ViewModel’s onCleared is called so that you don’t have to worry about holding the Job instance and explicitly cancel it. CoroutineExceptionHandler handles the exceptions being thrown at any Coroutine so that the errors can be propagated and displayed to the user.

Unit Testing Coroutine

We have introduced Coroutine to replace RxJava both on network and Db layer. Time to put them to test. We will use Mockito with mockito-kotlin for better mocking APIs. The test will run on RobolectricTestRunner to incorporate android resources into testing.

Testing Room With Coroutine
TvMaze stores the user marked favorite shows in Db. All the CRUD operations happen via FavoriteShowsRepository

To test FavoriteShowsRepository, we will create an in-memory database via Room.inMemoryDatabaseBuilder. The in-memory database provides faster CRUD operations on the database and also ensures that the database gets disappeared when the process is killed. To effectively test coroutines, we will rely on Coroutine test library(still in development). Let’s check the FavoriteShowsRepositoryTest class and understand the flow:

We mock the context and provide it to Room to create the database. Mocking of context is possible by mockito-inline which helps in mocking final classes. With the database, we get the mock showDao and create our repository. runBlocking runs the new coroutine on the current thread until its completion. We launch the new Coroutine or run the suspending function in this block. We have used Truth library that provides better and more expressive assertions.

Testing ViewModel
It was a little tricky to test HomeViewModel because of Coroutines getting integrated into the android lifecycle aware components. Let’s check the test and understand each step one-by-one:

InstantTaskExecutorRule is android architecture testing component which executes the task in the same thread. MainCoroutineRule sets the main Coroutines Dispatcher to TestCoroutineScope for better control over coroutines execution in unit testing. HomeViewModel is dependent upon TvMazeApi and FavoriteShowsRepository. So, we mock the required dependencies and then use @InjectMocks to mock HomeViewModel. To test, if the home screen is loaded with shows and without favorites, we first stub network and DB calls. Then, we verify whether the loader is shown or not. pauseDispatcher pauses the execution of a coroutine to verify the initial Loading state. After verification, we resumeDispatcher to proceed with execution of pending coroutine actions.

Initially, when HomeViewModelTest was run, it failed because of the way the tests were written. Read more here. Then after further readings and listening to an awesome Podcast, it was found that we don’t explicitly have to worry about context-switching with Retrofit and Room. The libraries that come with Coroutine support, owner the suspend functions and move them off the UI thread by their own. Thus, they reduce the hassles of handling thread callbacks by the developers in a big way. The end result is pretty neat asynchronous calls written in a synchronous manner with no callbacks and better readability.

UI Testing — Robot Pattern

Robot Pattern was introduced by Jake Wharton to provide a better separation of concern for UI testing. The pattern aims at properly managing the UI test by separating the What and How of it. Meaning, What is to be tested should not concern as How it is to be tested. Both should have clear separation so that maintenance of UI test becomes easy.

Say, we want to test the Home Screen, whether it shows the list of Popular Shows, and Add to Favorites are working fine or not. This is the What of Robot Pattern. The How of this will be taken care of by HomeRobot which handles all the verification/assertions/actions via Espresso.

LoadingIdlingResource tells Espresso to wait until the list ofshows is fetched and displayed on the screen. This is achieved via the visibility of the progress bar. As an alternative, some suggest to use Espresso in production code, but having a test code in production does not look good(Debatable). The final piece is our Kotlin Robot HomeRobot

This Robot is loaded with Kotlin goodness to remove the builder pattern and use language advanced primitives. Know more here.

Future is Bright

RxJava is a powerful library for asynchronous programming packed with tons of operators to get along. Coroutines are still new, and the Android community is slowly accepting it. Heavy development around Flow and Channels, are bridging this gap. Kotlin surely bags a punch with superb language features. With an awesome community, the road to the future of Android Application development looks even brighter. So why wait, go ahead and give a boost to your Android development with Kotlin now. 🚀

--

--