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

Tamás Kozmér
AndroidPub
Published in
5 min readOct 23, 2017

--

This is the last part of my series about Android testing. If you missed the first 2 parts don’t worry, you can understand this even if you haven’t read them. If you want to check them out anyway, you can find the links below.

In this part you will learn how to create UI tests in Espresso with fake data, how to mock dependencies with Mockito-Kotlin, and how to mock final classes in Android instrumented tests.

Writing Espresso tests with fake data

If we want to write UI tests, which always produce the same results, the most important thing we need to do is to make our tests independent from any data coming from the network, or from our local database.

In other layers we can easily achieve this by mocking the dependencies of the tested classes (as you have seen in the first 2 parts). This is a little bit different in UI tests. In the previous examples our classes got their dependencies in their constructor, so we could easily pass mock objects to the constructor. Android components are instantiated by the system, and they usually get their dependencies through field injection.

There are multiple ways to create UI tests with fake data. First let’s see how can we substitute the UserRepository with FakeUserRepository in our tests.

Implementing FakeUserRepository

The FakeUserRepository is a simple class, what provides us fake data. It implements the UserRepository interface. This interface is also implemented by the DefaultUserRepository, what provides us the real data in the app.

I think this code doesn’t need much explanation. We create a Single that emits a list of fake users. Although it’s worth to mention this part of the code:

val users = (1..10L).map

We can use the map function to create a list from a range. This can be very useful in cases like this.

Injecting FakeUserRepository into our test

Now we have our fake implementation of the UserRepository, but how can we use it in our test? With Dagger we usually have an ApplicationComponent and an ApplicationModule to provide the app level dependencies. We initialize the component in our custom Application class.

Now we will create a FakeApplicationModule and a FakeApplicationComponent, what will provide us the FakeUserRepository. In our UI tests we will set the component field to a FakeApplicationComponent.

Let’s see this in an example:

Since this component extends ApplicationComponent, we can use it instead of it.
We don’t need to provide anything other here, because most of the provided dependencies were used in the real UserRepository implementation.

The first two snippets were already explained above. The interesting part here is the MainActivityTest class. Let’s see what happens here.

In the setUp method we get a reference for the instance of our CustomApplication class, create our FakeApplicationComponent and then start the MainActivity.

It is important to start our Activity after we set the component. This can be achieved by passing an other constructor parameter to the ActivityTestRule constructor. The third parameter here is a boolean, what determines if the test runner should start the Activity immediately.

An example Espresso test

Now we can start writing some tests. I don’t want to get into the details of how to write test cases in Espresso, there are tons of tutorials out there, but let’s see a simple example.

First we need to add dependencies to our build.gradle. If we are using RecyclerView, we also need to add the espresso-contrib dependency on top of the usual espresso-core.

And our test looks like this:

What happens here?

First we find our RecyclerView then perform a click on it’s first (0 index) item with the help of RecyclerViewActions.

After we make an assertion, that a Snackbar is shown with the text User 1: 100 pts.

This is a pretty simple test case. You can find more example test cases in the projects Github repository. The code changes of this part can be found in this commit:

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

Mocking the UserRepository in UI tests

What should we do if we want to test the following scenario?

  • Load the first page of the data with success
  • Load the second page with an error
  • Verify if a Toast is shown on the screen, when we try to load the second page

We can’t use our fake implementation here, because it always returns success with a list of users. We can hack the implementation, that for the second page it returns a Single, what emits an error, but this is just not good. If we add another test cases, we need to hack it again and again.

We can mock the behavior of the getUsers method. For this we need to make slight modifications in the FakeApplicationModule.

Now we pass the UserRepository in the constructor, so in our test we can create a mock object, and build our component using that.

This is our modified test class. We are using the mockito-kotlin library, what I have already mentioned in the first part to mock the UserRepository. We need to add the following dependencies to build.gradle to use it.

androidTestImplementation "com.nhaarman:mockito-kotlin-kt1.1:1.5.0"
androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito:2.2.0'

Now we can modify the behavior of the mock. I created two private utility methods for this, that can be reused in the test cases.

The other change we need to make is to launch the Activity in the test case, after setting up the mock object, instead of doing it in the setUp method.

With this change our previous test case looks like this:

There are some more test cases in the GitHub repository, including error cases. Changes from this part can be seen in this commit:

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

Bonus: Mocking final classes in instrumented tests

In Kotlin every class is final by default, which makes mocking complicated. In the first part we have seen how can we mock final classes with Mockito.

Unfortunatelly this method doesn’t work in Android instrumented tests. There are a couple of other solutions, what can be used in this case. One of them is the Kotlin all-open plugin.

This is a compiler plugin, that lets us create an annotation, what will make the class open if used on it.

To use it we need to add the following dependency to our top level build.gradle file:

classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"

And add these lines to our app modules build.gradle file:

apply plugin: 'kotlin-allopen'allOpen {
annotation("com.myapp.OpenClass")
}

Now we only need to create our annotation in the package we specified:

@Target(AnnotationTarget.CLASS)
annotation class OpenClass

Example of the all-open plugin can be found in this commit:

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

We reached the end of our long journey to cover every piece of code in our app with tests. Thanks for reading this far, I hope you found these articles useful.

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 or you can reach me on Twitter.

--

--