Unit Testing with Mockito on Kotlin Android Project with Architecture Components
I love Kotlin, when i have started to write application with it i said, “wow cool”, it’s clean, it’s easy to read (sometimes 😁) and helps you to create a better code, but when i have started to write tests I encountered some problems.
In this article i will help you to solve these troubles and how mock a unit test.
About the Sample code
To explain how test a ViewModel with Mockito I have created a sample application to fetch the user’s repositories list, to do that i have create a ViewModel that use a Retrofit API to get data and a LiveData to expose these them to the view.
Important: in these sample I will not use Dagger to simplify code.
Mockito Troubles: the Right Dependencies
In Kotlin we can’t use the classic Mockito dependency of Java, because there are some limits, for example in my first experiment I have encountered these error when I tried to mock my own class:
Cannot mock/spy class eu.marcocattaneo.githubmockitosample.data.Repository
Mockito cannot mock/spy because :
— final class
the reason is when Mockito mocks an object normally it extends the requested class to create an instance, but all Kotlin classes are defined final in default, so the library can’t extend them.
You can fix it with the “open” modifier but it’s a workaround (and could be incompatible with your designed architecture), to solve these problem there is a specific dependency that allow mocking on Kotlin classes:
Another problem is the method Mockito.any() typically use to pass a parameter to a method of a mocked class, for example:
these produce an error:
java.lang.IllegalStateException: Mockito.any() must not be null
to fix it I use the Mockito version of Nhaarman:
this library exposes some Mockito alternative methods that allow that kind operations, look an example below:
Finally we don’t forget to add these line on build.gradle file that allow to mock some Android Api objects (in our case the Android Architecture Components).
unitTests.returnDefaultValues = true
If the exceptions thrown by Android APIs in the android.jar are problematic for your tests, you can change the behavior so that methods instead return either null or zero by adding the following configuration in your project’s top-level build.gradle file:
Below you can see my ViewModel, it’s quite simple: there are a method to get the user’s repositories and another to show/hide a progress dialog on the Fragment. The first makes a http request via Retrofit who response is posted to repositoriesLiveData, the second method is only to show/hide a ProgressDialog on the view via a LiveData (loadingLiveData).
Now we have the sample code and the libraries: a good starting point, let’s start!
ViewModel Testing with assertions
Test behavior of “repositoriesLiveData”
The scope of our test is to verify the behavior of the LiveData when the API request will produce data or a Throwable. First of all we need to create the test class (we can use the shortcut SHIFT+CMD+T) and add a setUp() method inside it.
The method with Before annotation will be run before all tests, in this method we will create the class instance to test (MainViewModel) and we will initialize all mocked elements with the function:
in our case it will create a mocked instance of UserService (the Retrofit implementation) but we can mark with Mock annotation all classes we need. After that we can write our first test!
Test the “fetchUserRepositories()” method with assertions
In the first test we want to check the value inside the repositoriesLiveData after the invoke of fetchUserRepositories() method on the MainViewModel:
With Mockito.then() we will create a behavior for the getRepositories() method on the mocked object userService so when it will execute the Observer of getRepositories will receive a list of fake object (look anyList() method) and it will post the value inside the repositoriesLiveData.
Now, our goal is to verify the content of this LiveData with a success HTTP call and to do that we need attach a mocked Observer on the LiveData, in this way we can collection the information inside it and with assert() verify its content.
Finally we run the method on the MainViewModel and write two assertions:
- Check if the LiveData values is not null
- Verify the LiveDataResult status, because with ah valid HTTP request is SUCCESS, but if the Retrofit service encounters an error we should find a ERROR status.
This is an another test with a failure example:
Verify method invocation: the Art of spy()
This is a way to test ViewModel’s methods and check the values produced inside the LiveData, but there is another way: you can verify if the method B() is called after the method A() and check the ViewModel behavior. To do that Mockito provides the Mockito.verify() method:
but you can use it only on a mocked object, if you try to test the sample above you it will produce this error:
Argument passed to verify() is of type MainViewModel and is not a mock!
Make sure you place the parenthesis correctly!
See the examples of correct verifications:
to fix that problem you need to use another Mockito method: spy(), this will allow us to call all the normal methods of the object while still tracking every interaction, just as you would with a mock.
Verify is quite simple: the first argument is our spied ViewModel, the second is the number of iterations (in our case are two: show + hide). On the Mockito.verify() we need to call the method to verify, in our case it’s setLoadingVisiblity().
You can find the official documentation here.
Thanks for reading!
I hope I helped you with this article, if you have other questions, please let me know! If you have liked this article don’t forget to 👏it. Thanks!
If you are interested the same approach is valid with MockK library. There is an article about it, see the link below: