Espresso Testing with Hilt and MockWebServer

Ashley Figueira
Pulselive
Published in
8 min readDec 7, 2020

Writing instrumentation tests on Android can be quite difficult, especially if you are just starting out. Mocking API calls or testing different error scenarios can quickly become a nightmare.

TL;DR Add a JSON file of your responses into a resource folder. Create a MockWebServer dispatcher which returns these JSON mocks. Replace your Base Url Module with a Fake Url Module using UninstallModules and InstallIn annotation. Launch your Fragment in a container. Write your test using Espresso. If you want a complete code example of a running test with mocked responses you can scroll down to the bottom.

Thankfully nowadays we have different libraries that help us quickly setup intrumentation tests in no time. There are many ways to setup instrumentation tests but in this article I am going to go through the steps on how you can easily setup intrumentation tests using MockWebServer and Dagger Hilt.

This tutorial will be using Dagger Hilt. If you haven’t used it before then I recommend you go read the documentation beforehand.

MockWebServer allows you to mock a web request. In other terms it allows you to return any response you want. You might be wondering why you’d need this?! It can be tricky to test that your app is handling empty and error cases properly when you’re dealing with a remote server. Servers are designed to be as reliable as possible, making it difficult to get error responses while testing. By using a mock server, you can easily mock error responses and see if the app handles them properly. All this, without making any changes to your actual server.

This tutorial assumes you are using Retrofit and OkHttp.

Sample App

Before we move on the sample app that we will be testing is a simple news list app that when clicking on an article opens the details view. The architecture of our app follows Googles Architecture guidelines.

The code is well structured and decoupled using Dagger Hilt dependency injection framework. It is important that the OkHttp client is provided via a module and that the base urls are also provided seperatly via another module.

In this tutorial we will be testing that we see a news list on the screen and upon clicking on a article we verify that it navigates to the details screen.

First Steps

First we are going to need to add our dependencies:

Hilt provides an application class that can be used for Android intrumentation tests. This class is called HiltTestApplication. We need to set this application using a custom test runner that extends AndroidJUnitRunner.

So lets go ahead and create our custom test runner:

The newApplication method provides the application instance you’ll use in the test (which is going to be our HiltTestApplication).

Now we need to configure our testInstrumentationRunner to use our custom runner that we created before. We configure this in the app’s build.gradle file:

Configuring MockWebServer

In this section we are going to configure MockWebServer instance and make it return any JSON response we want.

When we create a test with MockWebServer, the app’s services should not be pointing to a real URL. Instead it should use localhost. To set this up, you’ll need a mechanism to tell the app to use the real URL normally, but localhost when you run your tests. An easy way to do this is via Dagger Hilt.

You should provide you’re base url’s via a Dagger module (as mentioned before) and in our test class we can disable that Dagger module and add a fake module which provides the localhost as the url to be used with out services. We do so by using the UninstallModules and InstallIn annotations from Dagger Hilt.

Notice that we also need to annotate our test with HiltAndroidTest. This tells our emulator that the test being performed requires injection. You will also need to add a HiltAndroidRule. Both of these are required if you are using Dagger Hilt. The Dagger components and graph will not be created with out these.

To setup MockWebServer we’ll need to create an instance of MockWebServer and start it on port 8080. We need to make sure that after has finished running we stop the server.

Before we can start writing our tests, we need to define the reponses our MockWebServer will return. Create a resources folder inside the AndoridTest folder which is inside your src directory and place the JSON files that represent the responses you want. You can add success responses and failure responses.

MockWebServer can’t read the response from the JSON file directly. For this, we need to create a file reader to read the contents of the JSON file and convert them into a String object.

Now to dispatch the correct JSON file to the correct endpoint which we are hitting we can create a MockWebServerDispatcher. This allows us to return a specific JSON for a specific endpoint as well returning errors. This can be usefull if you are hitting multiple endpoints at once.

We have two different types of dispatchers. One where the success cases are handled and one for errors. The above snippet looks at the request path and returns the specific JSON file for the specific path.

Launching Our Screen

Normally to launch our Fragment one would use FragmentScenario, but Hilt currently does not support FragmentScenario. The main reason for this is that FragmentScenario under the hood launches an EmptyActivity and this Activity is not annotated with @AndroidEntryPoint as is required when working with Dagger Hilt. One workaround for this is to launch a Hilt activity and then attach your fragment to this Activity.

First thing we need to do is to create an empty Activity class and place it under the debug folder (app/src/debug/java). This way this empty Activity wont be present on a production build.

We can’t forget to declare this activity in the Manifest and annotate it with @AndroidEntryPoint.

Now what is left is to launch the Fragment that we are testing. We can do so by using the following function. You can specify the arguments that you want to pass into the fragment if any, the theme you want it to use and the fragment factory. You can also pass in any action that you want to invoke after the fragment is attached.

This code snippet was taken from the Android Architecture Samples.
Finally to Launch our screen we can do something like:launchFragmentInHiltContainer<NewsFragment>

Creating a TestNavController

To test that your fragment’s navigation operations is what you expect them to be Jetpack Navigation Components provides a TestNavHostController.

TestNavHostcontroller provides a set of APIs that allows you to set the graph to whatever graph you want to, set the current destination, verifying the back stack and much more. This is usefull because you can set the current desination to your Fragment under test so that you don’t need to navigate to it for testing.

We need to create an instance of TestNavHostController and assign it to the fragment. You can then via espresso navigate to a different screen by clicking a button and verify that the appropriate navigation action is taken.

We must call setGraph with the graph that we want to initialize the TestNavHostController. We should also set the current desination to the fragment under test, this is so that the nav controller is in the correct state before we start writing our tests.

So when you launch your Fragment it will look something like this:

You can find more information about testing navigation in the following link.

Writing Your Tests

Now that everything is set up, we can start writing Espresso tests. But there is one last thing we need to do before writing the actual tests.

API calls are asynchronous tasks, so you need a way to tell Espresso to wait for the API calls to complete. We’ll use an idling resource to solve this. Specifically OkHttp Idiling Resource from Jake Wharton.

As we are using Dagger Hilt, you can directly inject your OkHttp client into your test class and register it with our IdlingRegistry like so on your set up method.

Where okHttp is the actuall OkHttp Client that you inject into your test class. If you are using Coroutines or RxJava you might need to add idling resource for those aswell. You can find a good Idler for RxJava here made by Square.

Finishing Up

The Espresso testing framework is an instrumentation-based API and works with the AndroidJUnitRunner test runner. Espresso has basically three main components:

  • ViewMatchers allow finding the view in the current view hierarchy
  • ViewActions allow performing actions on the views
  • ViewAssertions allow asserting state of a view

Base Espresso test looks as follows:

onView(ViewMatcher).perform(ViewAction).check(ViewAssertion);

If you want to quickly gain some knowledge on Espresso you can find a nice Espresso cheat sheet here or instead you can use Barista. Barista is a library built on top of Espresso that provides a simple API, removing most of the boilerplate and verbosity of Espresso.

Putting everything together your test will look something like this:

It might look light a lot of code just to write one simple test but most of this can be abstracted away into a base class and you can reuse most of it for your other screens.

In this simple test we launch the news screen, click on the first article and check that it navigates to the news detail screen. In this case we will get the Success response from the RequestDispatcher that is definied in the set up method. If you wish to test error cases you can just set the MockWebServer dispatcher to the ErrorDispatcher in your test instead of the setup method.

If you want to learn more about testing using Hilt and MockWebServer here are some usefull links:

I hope you’ve enjoyed this blog post, and it’s helped you in some way. Please share and applaud this article. Thanks for reading and see you next time. ~Ashley~

I would also want to thank my teammates at Pulselive for proof reading this article and helping me become a better developer everyday.

--

--