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.
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.
Adding MockWebServer as a dependency
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 !
Here is what the application component looks like
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!.
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 :)
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.
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
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
Testmethod. It will be launched before the first
Beforemethod, and terminated after the last
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.
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
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
That is all ! Now you can tweak network calls easily in your instrumentation test :)
mohak1712 (Mohak Puri)
mohak1712 has 34 repositories available. Follow their code on GitHub.
If you find anything wrong do let me know and if you have a better approach do share in the comment section below !