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

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 TvFlix 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 this:

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 of the above to stand out as a better JSON serialization library. It provides first-hand Kotlin support with moshi-kotlin and codegen. Kotlin data classes are immutable and copy the method helps in creating the builder. Kotlin's @Parcelize helps to replace the AutoValue parcel extension 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 synchronously writing asynchronous operations. There are tons of articles that already explain 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 the user’s favorites. A user can mark any show as a favorite which gets stored via Room. Room library now comes with Coroutine support which means all the Dao operations can be suspended thus removing RxJava completely.

Now we are ready to load data for Home screen. HomeViewModel previously relied on RxJava heavily. When the onScreenCreated was called, two streams: one from API and the 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 the IO-bound coroutine via Dispatchers.IO and once the suspending function is complete, it will return the result 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 the coroutine when ViewModel onCleared is called so that you don’t have to worry about holding the Job instance and explicitly canceling 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 the network and DB layer. Time to put them to the 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

TvFlix 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 disappears 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 which provides better and more expressive assertions.

Testing ViewModel

InstantTaskExecutorRule is an Android architecture testing component that executes the task in the same thread. MainCoroutineRule sets the main Coroutines Dispatcher to TestCoroutineScope for better control over coroutine 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, own the suspend functions and move them off the UI thread on their own. Thus, they reduce the hassles of handling thread callbacks by the developers in a big way. The result is pretty neat asynchronous calls written synchronously 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 that what is to be tested should not concern How it is to be tested. Both should have clear separation so that maintenance of UI tests becomes easy.

Say, we want to test the Home Screen, whether it shows the list of Popular Shows, and whether Add to Favorites is 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 using 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 them. 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. 🚀