Unit Test API Calls With MockWebServer

Shaishav Gandhi
AndroidPub
Published in
5 min readDec 18, 2017

--

One of the great benefits of having MVP architecture/Clean architecture is the separation of concerns and the testability that each layer provides. Each layer takes care of things that are specific to it : for example, Presentation layer will take care of things related to presentation logic. For example making calls to Data layer, getting a result and then setting it to the View. Or limiting the amount of data to be shown. Data layer will expose an API which can be consumed by any Presenter and returns the data. The Data layer will contain all logic related to caching (if any), getting data from API if necessary, sanitizing API response (if required) etc. View layer is supposed to be really dumb. It will only intercept clicks/user events and ask the Presenter what to do and then just display whatever the Presenter tells it to display. This separation of concerns is very friendly to writing unit tests since each layer can have mocked dependencies and we can test for happy-cases as well as disastrous ones!

Let’s take a simple example of a screen which shows a list of blogs that are fetched from a remote server. I’m using RxJava2 and Retrofit, OkHttp for this example. There are a ton of other great libraries like Dagger which would help with testability too, but that is out of the scope for this post.

I meant “Let’s code” 😅

Your Presenter would look something like this at a bare minimum :

You have a BlogPresenter which is initialized with a BlogRepository and BlogView which is the contract between the View and the Presenter. Presenter makes a call to the repositories’ blogs() method which presumably returns an Observable<List<Blog>>. In the onNext method, you set the list of blogs to the view. You add this observable to a CompositeDisposable and then dispose it off in your appropriate Lifecycle event. This is all pretty basic MVP.

You will probably write a JUnit test for the presenter, which will be something like this :

These are sample two tests that can be written for the Presenter. One which deals with a successful response and one which deals with an error.

This is all well and good. We’ve now added test coverage to our Presenter. So from the Model, View and Presenter : we’re done with Presenter.

What about tests for the Model?

I found that I was particularly lazy when it came to testing network requests, which is a pretty bad thing. Because, as far as I can see, the most error prone part of your application is probably the network request. The server could be unavailable, the request could time out, there could be malformed JSON returned in the response which will throw our TypeAdapters if you’re using Retrofit. The possibilities are endless.

Enter MockWebServer

Thankfully, the great guys at Square who made OkHttp and Retrofit also have a testing library called MockWebServer which is part of OkHttp.

With MockWebServer, you can easily mock server responses including body, headers, status code etc.

Include it in your project by adding this to your build.gradle file.

testImplementation 'com.squareup.okhttp3:mockwebserver:(insert latest version)'

Brief Overview

MockWebServer has a very simple API which lets us setup a mock server which will intercept our requests and return whatever mocked response that you want it to return.

You can easily create a server with :

val mockServer = MockWebServer()

Start server :

mockServer.start()

Mock a response using a composable API:

val mockedResponse = MockResponse()
mockedResponse.setResponseCode(200)
mockedResponse.setBody("{}") // sample JSON

Enqueue request so that mocked response is served :

mockServer.enqueue(mockedReponse)

You can also do neat things like intercept the request that was made

val recordedRequest = mockServer.takeRequest()
mockedRequest.path // /blogs

Now that we know about MockWebServer, let’s see what our BlogRepository actually looks like.

It takes in a blogService in the constructor, which is created using the Retrofit instance.

The BlogService looks simply like this :

Now that we know the BlogRepository, let’s start writing some tests. But before that, we’ll have to setup our test so that the mock server can start listening to requests and then shut down when it’s done.

Here, we’ve just laid down the groundwork to start writing our test. We have some initializations that we will need to make of MockWebServer, BlogRepository and BlogService. In our setUp() method we make these initializations by getting an instance of OkHttpClient and Retrofit and using those to create our BlogService and finally supplying BlogService to our BlogRepository.

We also wrote a tearDown() function which will be executed after all the tests have finished executing. Here we simply shut down the server that we created and started by calling mockServer.shutdown().

Onto some tests

As explained before, we setup a mockResponse, enqueue the mockResponse so that it’s served. Subscribe to it with our testObserver and then we make some assertions saying, there shouldn’t be any error and there should be only one list that is emitted.

Wait wait wait. Where did the getJson() come from?

The getJson(path = "json/blog/blogs.json") is a utility method which helps us store our mocked responses as JSON files.

In your test directory, you can easily create a resources directory which is used to you-guessed-it-right, store resources.

The utility method to actually read the JSON file is something as follows:

/**
* Helper function which will load JSON from
* the path specified
*
* @param
path : Path of JSON file
* @return json : JSON from file at given path
*/
fun
getJson(path : String) : String {
// Load the JSON response
val
uri = this.javaClass.classLoader.getResource(path)
val file = File(uri.path)
return String(file.readBytes())
}

You can keep a lot of this common stuff in a Base class which other API tests can extend.

Simulating Network Conditions

That was great for our happy-case where we get the appropriate JSON back. But what if our server is down? The test for that would be something like this:

SocketTimeoutException?

MockWebServer got you covered.

Remember our mockResponse? Just add this :

mockResponse.throttleBody(1024, 1, TimeUnit.SECONDS)

Which basically means only send 1024 bytes per second.

And then, just add this simple line to our okHttpClient initialization

val okHttpClient = OkHttpClient.Builder()
.connectTimeout(2, TimeUnit.SECONDS) // For testing purposes
.readTimeout(2, TimeUnit.SECONDS) // For testing purposes
.writeTimeout(2, TimeUnit.SECONDS)

.build()

The test would look something like:

And that’s it!

You can write a bunch of tests like these and simulate similar conditions! This way, you can easily test the Model part of your application and I would argue the most important and error prone part of your app : Network Requests.

--

--