Introduction to Android Testing – Part 3

In the previous two blog posts I covered how to get setup with testing in Android and we created a sample app that we will be continuing to develop in this blog post. If you missed those two posts, I suggest reading part 1 and part 2.

In this post, we will look at getting a list of users from the Github API and writing unit tests for it. We will be starting from the following repo at this checkpoint.

Create Web Service Calls

To consume the Github API we will be using Retrofit and RxJava. I am not going to explain RxJava or Retrofit in this series. If you aren’t familiar with RxJava, I suggest reading these articles. If you haven’t used Retrofit before I suggest reading this.

In order to get a list of users for a search term, we will need to use the following endpoint (I am also applying a page size of 2 for this demo because of API call limiting):

To get more user information (such as a user’s bio and location), we need to make a subsequent call:

  1.  To start consuming these endpoints, we should create the JSON objects that they are returning and include them in our project. I normally generate them online here. Let’s create the following classes:  User class and UsersList class:


    Once the models are created, navigate to the GithubUserRestService. This is where we will create our Retrofit calls.

    The first network call will perform a search to get a list of users and the second network call will get more details about a user.[adwords_square]
  2. Navigate to  UserRepositoryImpl. This is where we will combine the two network calls and transform the data into a view that will be used in the front end. This is using RxJava to first get a list of users for the search term and then for each user it does another network call to find out more of the user’s information. (If you had implemented this API by yourself, I would try make one network call return all the required info – as discussed in my Reducing Mobile Data Usage Talk).

    In the above code, I am creating an observable by using Observable.defer() , this means that the observables code will only run once it has a subscriber (Not like Observable.create() which run when it is created). As corrected from the comments below, Observable.create() is an unsafe RxJava API and it shouldn’t be used.

    When there is a subscriber, the githubUserRestService  is called to search with the searchTerm  provided. From there, I use concatMap  to take the list of users, emit them one by one into a new observable that then calls githubUserRestService.getUser() for each user in that list. That observable is then transformed into a list of users.

    A retry mechanism has also been defined on these network calls.  retryWhen() will retry the observable when an IOException is thrown. An  IOException is thrown by Retrofit when a user has no internet (you might want to add a terminating condition such as only retrying a certain number of times).

    You might notice that I am using lambda expressions in the code, you can do this by building your app with the new Jack toolchain. Read about how to enable building for Java 8 on Android here.

    Now we have a repository and two network calls to get a list of users! We should write tests for the code we have just written.

Unit Testing – What is Mockito?

In order to unit test our repository object, we are going to make use of Mockito. What is Mockito? Mockito is an open source testing framework for Java released under the MIT License. The framework allows the creation of test double objects (mock objects) in automated unit tests. (Wikipedia).

Mockito allows you to stub method calls and verify interactions with objects.

When we write unit tests, we need to think of testing a certain component in isolation. We should not test anything beyond what that class’ responsibility is. Mockito helps us achieve this separation.

Okay, let’s write some tests!

Writing Unit Tests for UserRepositoryImpl

  1. Select the  UserRepositoryImpl class name and press “ALT + ENTER”. A dialog will pop up with the options to “Create Test”. Select that option and a new dialog will appear:Create Test Dialog
  2. You can select to generate methods but I generally leave the options unselected. It will then ask you to select a directory where the test should be placed. Select the “app/src/test” directory as we are writing a JUnit test that does not require an Android ContextSelect Test Directory - Automated Testing Android
  3. Now we are ready to set up our unit test. To do this, create a UserRepository object. We will also need to create a mock instance of GithubUserRestService as we won’t be directly hitting the API in this test. This test will just confirm that the transformations are done correctly within the UserRepository.  Below is the code to set up our unit tests:

    The method that is annotated  @Before will run before any unit test and it ensures that the Mock objects are setup before trying to use them.  We call MockitoAnnotations.initMocks()  in the setUp()  method and then create an instance of the UserRepository  using the mocked out github service.[adwords_square]
  4. The first test we will will write will test that the GithubUserRestService  is called with the correct parameters. It will also test that it returns the expected result. Below is the example test I have written:

    This test is split up into three sections namely: given, when, then. I separate my tests like this because it ensures your tests are structured and gets you thinking about the specific functionality you are testing. In this test, I am testing the following: Given the Github service returns certain users, when I search for users, the results should return and transform correctly.

    I find naming of tests is also quite important. The naming structure I like to follow is the following:

    [Name of method under test]_[Conditions of test case]_[Expected Result]

    So in this example, the name of the method is searchUsers_200OkResponse_InvokesCorrectApiCalls() . In this test case, a  TestSubscriber is subscribed to the search query observable. Assertions are then done on the TestSubscriber  to ensure it has the expected results.

  5. The next unit test will test if an IOException  is thrown by the search service call then the network call will be retried.

    In this test, we are asserting that the githubUserRestService  was called twice and the other network calls were called once. We also assert that there were no terminating errors on the subscriber.

    Final Unit Test Code for UserRepositoryImpl

    I have added a few more tests than the ones described above. They test different cases but they follow the same concepts as described in the previous section. Below is the full test class for UserRepositoryImpl :

Run the Unit Tests

Now after writing these tests, we need to run them, see if they pass and see how much of the code is covered by tests.

  1. To run the tests you can right click on the test class name and select “Run UserRepositoryImplTest  with Coverage”Run unit tests with coverage
  2. You will then see the results appear on the right hand side of Android Studio. Code Coverage Report - Unit test Android Studio

We have 100% unit test coverage on our UserRepositoryImpl  class. Yay!

In the next blog post, we will take a look at implementing the UI to display the set of search results and writing more tests for that. Be sure to subscribe so you don’t miss the next blog post in this series!

Subscribe to Blog via Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.


If you enjoy my posts, please consider buying me a cupcake to keep them coming.
[buy_cupcake]

Links:

Defering observable code until subscription in RxJava

RxJavas retryWhen() and repeatWhen()

Github user search app on Github

Retrofit 2 – Mocking HTTP Responses

In the previous post, I discussed implementing a custom Retrofit Client to mock out different HTTP response codes. This mechanism works well for Retrofit versions 1.9 and below but has its drawbacks. After trying out Retrofit 2, I have adjusted the previous sample and managed to achieve the same results (with some improvements 😀).

In order to test your android apps, one thing that normally gets frequently overlooked is the apps ability to handle different server responses. What if your server goes down for a while? Does your app fall over with it – or does it gracefully recover? Things like this are difficult to emulate with real servers, which is why mocking responses is such a great way to ensure your app is awesome.

In this example, we will look at creating an app that retrieves a quote of the day from a web service and displays it to the user. We will also add a failure mechanism to the front end to show the user a retry button if something goes wrong. We will also look at testing these failure mechanisms.


Example 1:

  1. Create a Rest Service interface that will be used with Retrofit.
  2. Ensure your activity calls the Retrofit Service that you have just created. In the code below, the service gets created and the activity asynchronously calls getQuoteOfTheDay() . When a successful response is received from the server, the quote is displayed otherwise a retry button is shown with an error message.
  3. You might wonder, how do we test that the retry button is properly shown when the server is down without turning the server off? 😁 Using Espresso and Retrofit MockWebServer we can easily achieve this. Create mock JSON and store it in your androidTest  folder.
    Success Response Sample JSON:

    404 Not Found Sample JSON:
  4. Below is the sample for testing the positive case (a quote is displayed to the user) and the negative case. A retry button is displayed to the user when the server is returning an error. You might want to do some other logic when there is an error but for simplicity we will just display a retry button.

    From the sample above, we can see that the method setUp() is starting up the MockWebServer and setting the BASE_URL  of the entire app to point to the local mock server’s url.
    The test testQuoteIsShown()  is enqueuing a 200 OK response on the mock server, with the JSON from the file we defined previously as the body. We then launch the Activity. Using Espresso, we ensure that the retry button is hidden and the quote is displayed.
    The test testRetryButtonShowsWhenError() does a similar thing, except it queues up a 404 response, ensures that the retry button is shown and that the text “Quote Not Found” is displayed.
  5. As you can see from the above sample, we have achieved the same results as the previous post, but using Retrofit 2.

Pros of testing this way:

  • Really simple to test different HTTP Status Codes
  • Not much code needed to emulate the different responses

Cons of testing this way:

  • Difficult to dynamically create the responses

Example 2:

In the sample app, I have also included another way to test your Rest API by implementing the interface defined at the very beginning.

  1. Create a mock implementation of your API methods. In this stub, we create a dummy quote and return that quote every time. This is defined in the androidTest folder.

    2. Create another mock implementation that will return the error scenario.

3. Create a test which will test the API response parsing.

As you can see, the implementation that we mocked out previously is now being used in the test. In the  setUp()  method the MockRetrofit object is created. This MockRetrofit object wraps around the dummy implementation that we created and emulates a network call by adding delays onto the calls.



The testRandomQuoteRetrieval()  uses the first mock implementation to test the positive scenarios, whereas  testFailedQuoteRetrieval()  uses the second mock implementation which will return a 404 error.

This can be used to test operations on the API and test front end scenarios such as adding items to the server without sending it off to the server unnecessarily.

Pros of testing this way:

  • Can create rich data to return to the tests which means testing can be more meaningful
  • Easy enough to set up
  • JSON isn’t static

Cons:

  • Have to create new API implementations for different error scenarios
  • Hard to see from a glance what the API returns

I think a combination of the two mechanisms described above can definitely cover most scenarios that you would need to test in order to make your app stable. What are your thoughts?

You can check out the full project here on Github.


If you enjoyed this blog post – please consider buying me a cupcake!

No. of Cupcakes


Mocking API Responses using a Retrofit Client in Android (Retrofit 1.9)

Writing test cases in Android can be quite a daunting task, especially the handling of different server responses. Testing error responses can be quite problematic and your app might not cover all the different scenarios.

If you are using Retrofit 1.9 and below to do your network calls, testing error responses can be quite simple. There are a couple of ways to test Retrofit calls. In this post we will look at making the Client return different responses which are stored in .JSON files within the test project.

Firstly, create a  RetrofitMockClient within your test project that will replace your response with the custom responses that you define.

Create the response JSON for the service you wish to test. In this example, we will be testing a service that returns random quotes for a logged in user. The API call can have HTTP status codes such as 401 Unauthorised, 200 OK, 500 Internal Server Error etc.

For example, a 401 Unauthorised Response could look like this:   quote_401_unauthorised.json

These JSON files should be created and saved in the /androidTest/assets  folder.

Then create a test in the androidTest  folder:

There you have it, your custom Retrofit Client will now return the JSON that you have specified. Yay!

Pros of testing this way:

  • Simple & easy to understand.
  • Possible JSON response is stored within the test project – new developers will easily understand what the services are doing and what the expected server responses look like.
  • Easy way to test different response codes and how your app reacts to them.
  • It is also possible to test different returned headers with this mechanism.
  • No need to run a mocking web server.

Cons of testing this way:

  • Hard to test complex server responses (i.e. dates that should change dynamically based on current date).
  • Limited to the JSON that you define.
  • Hard to emulate network speed.

In the next post, we will look at using other mechanisms, like Mockito, for testing our APIs.

Code on Github