Complete example of testing MVP architecture with Kotlin and RxJava — Part 1

Recently I created a playground project to learn more about Kotlin and RxJava. It’s a pretty simple project, but there was one part, where I had some struggle: testing.

Testing in Kotlin can have some pitfalls, and since it’s quite new, there are not so much examples out there. I thought it would be a good idea to share my experience to help you avoid my mistakes.

The app follows a basic MVP architecture with the repository pattern. It uses Dagger2 for dependency injection, and RxJava2 for data flow.

The repository provides the data from network or local storage, depending on different conditions. We use Retrofit for network calls, and Room for local database.

I won’t get into details about the architecture and these tools. I think most of you are already familiar with them. You can check out the starter project in this commit:

https://github.com/kozmi55/Kotlin-MVP-Testing/commit/ca29cad1973cd434ffb0b0d23c4465fc54e05c0b

We will start with testing the database and then go upwards in the layers.

For the database we are using Room Persistence Library from the Android Architecture Components. It is an abstraction layer over SQLite, that reduces boilerplate code.

This is the easiest part. We don’t need to do anything specific regarding Kotlin or RxJava. Let’s look at the code of the UserDao interface first, to decide what should we test.

The getUsers function requests the next 30 users from the database depending on the page.

The insertAll inserts all the users in the list.

We can see a couple of things here, what needs to be tested:

  • Check if the inserted users are the same as the retrieved users.
  • Check if the retrieved users are ordered correctly.
  • Check if we insert a user with the same id, it will replace the old record.
  • Check if we query a page, it will have a maximum of 30 users.
  • Check if we query the second page, we will get the correct number of elements.

The below code snippet shows the implementation of the 5 cases.

In the setup method we need to configure our database. We use Room’s in memory database to create a clear database before each test.

The tests are pretty simple here and don’t need further explanation. The basic pattern we follow in each test looks like this:

  1. Insert data to the database
  2. Query data from the database
  3. Make an assertion about the retrieved data

We can use the functions from the Kotlin Collections API to simplify the creation of test data, like this part of the code:

val users = (1..40L).map { User(it, "Name $it", it *100, "url") }

We create a range, then map it to a list of users. There are multiple Kotlin concepts used here: ranges, higher order functions, string templates.

Commit: https://github.com/kozmi55/Kotlin-MVP-Testing/commit/8cebc897b642cc843920a107f5f0be15d13a925c

For the repository and interactor we are going to use the same tools.

  • Mockito for mocking the dependencies of the classes.
  • TestObserver for testing Observables (Singles in our case)

But first we need to enable the option to mock final classes. In Kotlin every class is final by default. Fortunately Mockito 2 already supports mocking final classes, but we need to enable this.

We need to create a text file in the following location: test/resources/mockito-extensions/ with the name org.mockito.plugins.MockMaker and with the following text: mock-maker-inline

Place of the file in Project view

Now we can start writing our tests using Mockito. First we add the dependency to the latest version of Mockito and JUnit.

testImplementation 'org.mockito:mockito-core:2.8.47'
testImplementation 'junit:junit:4.12'

The code of the UserRepository is shown in the following snippet:

In the getUsers method we create a Single, what will emit the users or an error, if occurs. Depending on different conditions the shouldUpdate method decides, whether the users should be loaded from network or from local database.

One more thing to note is the CalendarWrapper field. It’s a simple wrapper with one method, what returns the current time. With the help of this we can mock the time in our tests.

So what should we test here? The most important thing to test here is the logic behind the shouldUpdate method. Let’s write some tests for it.

The way to test this is to call the getUsers method, and on the returned Single call the test method. The test method creates a TestObserver and subscribes it to the Single.

The TestObserver is a special type of Observer, that records events and allows making assertions about them.

We also have to mock the dependencies of the UserRepository, and stub some of their methods to return the data, what we specify. We can do this with Mockito the same way as in Java, or with the Mockito-Kotlin library by Niek Haarman. We will use Mockito in this example, but you can check the Github repository if you are curious.

If we want to use the when method from Mockito, we need to put it between backticks, because it is a reserved word in Kotlin. To make this look better, we can import the when method with a different name using the as keyword.

import org.mockito.Mockito.`when` as whenever

Now we can use the whenever method for stubbing.

Above we can see the code of UserRepositoryTest. We are using Mockito annotations in this example to initialize the mocks, but it can be done in different ways. Every test consists of 3 steps:

  1. Specify what values should the stubbed methods return. We use the setUpStubbing private method to avoid boilerplate code in our tests. We can call this method in every test case with different parameters, depending on what state is under test. Kotlin’s default arguments can be really helpful here, because sometimes we don’t have to specify every parameter.
  2. Call the getUsers method, and acquire a TestObserver by calling the test method on the returning Single.
  3. Make some assertions on the TestObserver or on the mock objects to verify the expected behavior. In this example we use the assertNoErrors method, what verifies that the Single doesn’t emit an error. The other method we use is the assertValue. With the help of this we can make assertions about the value emitted by the Single. The way to do this is to pass a lambda to the assertValue method, which returns a boolean value. If it returns true, the assertion will pass. In this case we verify, that the emitted list contains 1 element. There are plenty of other methods to make assertions on the TestObserver, these can be found in the documentation of BaseTestConsumer, what is the superclass of TestObserver.

Changes can be found in this commit:

https://github.com/kozmi55/Kotlin-MVP-Testing/commit/17fc4645bb446879a0e44560c19d6c2c36810a89

The method of testing the GetUsers interactor is similar to the one that we used to test the UserRepository.

GetUsers is a really simple class, it’s purpose is to transform the data coming from the data layer to data, what represents the users in the presentation layer.

We use some transformations here from RxJava and also from Kotlin Collection API to achieve the desired results.

Let’s see how our tests look like.

The only difference here is that we are creating a fake Single object, what will be returned from the repository’s getUsers method. We are passing the UserListModel what should be emitted by the Single to the setUpStubbing method, where we create our fake Single, and set it as the return value of the repository’s getUsers method.

The rest of the code uses the same concepts, that we have seen in the UserRepositoryTest.

Commit after writing GetUsersTest:

https://github.com/kozmi55/Kotlin-MVP-Testing/commit/49652a53813f004b2c11f962d8ba5666575365fc

That’s all for the first part. We learned how to tackle some common problems while testing in Kotlin and using RxJava, how to utilize some Kotlin features to write simpler tests, and also took a look at testing the Room database.

In the second part I will show you how to test the Presenter with the help of TestScheduler, and how to make UI tests with fake data using Espresso. Stay tuned.

Thanks for reading my article.

If you liked it, hit the clap button or share it with fellow Android developers.

If you have any questions or suggestions leave a comment below.

Android Developer @ Instructure

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store