Instrumentation testing with MockWebServer and Dagger2

Mohak Puri
MindOrks
Published in
5 min readJun 30, 2018
Credit : Google

Testing on android is tough specially when it comes to instrumentation testing. Handling UI changes along with tons of network calls becomes a nightmare with Espresso.

Thanks to the guys at Square we have MockWebServer that allows you to mock web request and helps a ton with instrumentation test. Rather than making an actual network call MockWebServer allows you to mock the response of the network request. So in a nutshell, when you run your test MockWebServer intercepts your network call providing you with the data you mock data.

Why?

So it comes down to this. Why on earth do we need this ? Well when it comes to testing it will make your life damn easy. Let us see how !

I will be using Dagger from here. If you don’t know what it is then you should definitely google some tutorials before proceeding further. Also the focus is on MockWebServer and I wont be explaining Dagger.

Enough talking! Let’s code

Adding MockWebServer as a dependency

dependencies {
...
androidTestImplementation 'com.squareup.okhttp3:mockwebserver
:latest-version'
}

Let us quickly go through some dagger stuff. Let us start with our custom application class. Here we create our application component. That’s all !. Don’t forget to add this to the manifest file !

android:name="MyTestingApp"
MyTestingApp.java

Here is what the application component looks like

ApplicationComponent.java

Now let’s check the Application Module. Careful! Look at the Retrofit component and its baseUrl. You can ignore the OkHttp client as it has nothing to do with testing!.

ApplicationModule.java

So we are done with the basic structure of the application. Now let us dive into the testing part !

All the files from now must be a part of android test folder and not the main java folder !

First, create a new application class that extends your previous class (the one that is mentioned in your manifest file). If you haven’t noticed yet we are providing a different module to our application component this time. It is not the ApplicationModule but TestApplicationModule.

Do not replace the manifest file with this class. Just leave the manifest file alone we don’t need it :)

UiTestApp.java

Here is what the TestApplicationModule class looks like. Yes it extends ApplicationModule but it’s getRetrofit() looks a bit different. Rather than using the API endpoint that your application is using we are using some http://localhost:8080/ . This will make complete sense just a little more patience !

Let us create our custom runner class that extends the AndroidJUnitRunner.

MockRunner.java

Let me bring you focus to this line

return super.newApplication(cl, UiTestApp.class.getName(), context);

When we use this MockRunner for testing our application, rather than using MyTestingApp for creating our application component the test will use UiTestApp.

So how does that help ?

Basically this is what happens - Since you are using UiTestApp for creating your application component and the application component is using TestApplicationModule for creating the dependencies your Retrofit baseUrl will be http://localhost:8080/ when you are testing your application. To conclude -

When you are running your tests you will be hitting http://localhost:8080/ for the API request instead of the real one !

I hope it all makes sense ! and yes for all of this to work make changes to your build.gradle file

//change
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//to
testInstrumentationRunner "{com.your.package.name}.MockRunner"

Now that all the setup is done let us dive into MockWebServer

/** Let us first see some basics **/MockWebServer webServer = new MockWebServer();// starts the server at port  8080
webServer.start(8080);
// useful when handling multiple endpoints and want to send some specific data for case
webServer.setDispatcher(new MockServerDispatcher());
//shutdown server
webServer.shutdown();
That is all we need for now !

Case Study - A basic application that sends an API request as soon as you open the application. Initially it shows some progress bar that hides once the response is received. It also has a retry button that sends the request in case it fails for some reason.

App Opens -> ProgressBar -> Result -> ProgressBar hides

Now lets see the test ! Nothing much happening here before starting the test we create the server and start it at port 8080. Once the test is done we shutdown the server. One thing here to notice is the 3rd parameter to the ActivityTestRule. From the documentation -

boolean: true if the Activity should be launched once per Test method. It will be launched before the first Before method, and terminated after the last After method.

So basically with false we will have to launch the activity by our self. The system is not going to do that for us. This is important if you have an API request as soon as you open your application. This allows us to start the server and attach dispatcher(will see later) before our activity starts or in other words before our activity makes an API request. Thanks to ISHAAN KAKKAR for helping me with this !

Note : In case your activity does not have an API request as soon as it opens you can skip the launchActivity parameter.

MainActivityTest.java

Lets say that our API has multiple endpoints. With dispatcher we can tell what data is to be returned when our application hits those endpoints. Not only that we can easily generate error conditions for our application by setting appropriate value of response code. Here is what a dispatcher may look like.

Note : Our case study has just one endpoint. This is just an example of how you can handle multiple endpoints. You can also use json files to set the body through GSON

MockServerDispatcher.java

Let us see test our app in Happy Condition i.e everything goes fine and there are no errors. In happy condition the progress bar is invisible, the errorUI is invisible, the retry button is invisible and the textview showing the mock response is visible. Make sure to add the following line -

// launches target  activity
activityRule.launchActivity(new Intent());
Happy case testing

That is all ! Now you can tweak network calls easily in your instrumentation test :)

If you find anything wrong do let me know and if you have a better approach do share in the comment section below !

--

--

Mohak Puri
MindOrks

Engineering @INDmoney | ex GO-JEK | GSoC 2018 @openMF | Mobile | Backend | mohak1712 everywhere