Testing REST APIs Using MockWebServer
Learn how to mock a REST API with MockWebServer and easily test your business logic using Espresso to check how your UI handle success or error responses. By Subhrajyoti Sen.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Testing REST APIs Using MockWebServer
20 mins
- Getting Started
- Why Use a Mock Server to Test API Calls?
- Why Use MockWebServer Over Manual Client Mocking?
- Configuring MockWebServer
- Changing the API URL
- Creating a Test Runner
- Using Espresso
- Setting up MockWebServer
- Setting up a Mock Response
- Creating a JSON Response
- Creating a File Reader to Read the Response
- Writing the Tests
- Setting up an Idling Resource
- Testing a Successful Case
- Testing a Failed Response
- Where to Go From Here?
Using Espresso
Espresso is a testing framework that Android provides to write UI tests. It allows you to emulate user interactions while running tests and verify if your views respond as expected.
You can test MockWebServer even without Espresso. But this tutorial uses it to demonstrate a complete testing scenario.
Before starting to write the test, it’s helpful to review the contents of activity_main.xml. It contains three main views:
- ProgressBar: Displays the loading status.
- TextView: Shows the error message.
- RecyclerView: Displays the list of characters.
Create a file MainActivityTest.kt in the androidTest folder and add this to it:
package com.company.android.whatsthememe
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.After
import org.junit.Before
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class MainActivityTest {
@Before
fun setup() {
}
@After
fun teardown() {
}
}
This is the basic structure of an Espresso test. You annotate a class with @RunWith(AndroidJUnit4::class)
to specify that AndroidJUnit4
is the runner for this test.
A Before
annotation on a method specifies that the method will run before every test. Any method with @After
will run after every test. Use these annotations to keep initialization logic common to all tests.
Setting up MockWebServer
You’ll need to add the Gradle dependency for MockWebServer first to use instances of MockWebServer. Open the app-level build.gradle and add the following line below the corresponding // TODO 2
at the end of the file:
androidTestImplementation "com.squareup.okhttp3:mockwebserver:4.9.3"
Click Sync Now and let Gradle finish syncing.
Next, you’ll create an instance of MockWebServer. Add the following line before setup()
in MainActivityTest.kt:
private val mockWebServer = MockWebServer()
Place the cursor on the word MockWebServer
and press Alt-Enter on Windows or Option-Return on Mac. Then, choose Import to automatically add the missing import line and fix the build.
You have to start the server before every test and stop it after every test. You can do this by modifying setup()
as follows:
@Before
fun setup() {
mockWebServer.start(8080)
}
This starts the mock server on the 8080 port.
Next, modify teardown()
to stop the server:
@After
fun teardown() {
mockWebServer.shutdown()
}
And that’s all it takes to get MockWebServer ready. All you need to do now is to set up some mock responses and write some tests.
Setting up a Mock Response
Before writing your tests, you must define the response your mock web server will return. To find the structure of the Imgflip API’s response, you need to make a real API call first.
Creating a JSON Response
Open a web browser and paste the following URL: https://api.imgflip.com/get_memes.
The response will be in the following format:
{
"success": true,
"data": {
"memes": [
{
"id": "181913649",
"name": "Drake Hotline Bling",
"url": "https://i.imgflip.com/30b1gx.jpg",
"width": 1200,
"height": 1200,
"box_count": 2
},...
]
}
}
Keep the browser open; you’ll need it later.
If it doesn’t already exist, create a directory named assets in your app
module. It’ll look something like this:
Create a file in the assets directory and name it success_response.json. This file will contain the response the mock server will return.
Switch to your browser, copy the first eight items from the response and paste them into this file. Now, your success_response.json file looks something like this:
{
"success": true,
"data": {
"memes": [
{
"id": "181913649",
"name": "Drake Hotline Bling",
"url": "https://i.imgflip.com/30b1gx.jpg",
"width": 1200,
"height": 1200,
"box_count": 2
},
{
"id": "87743020",
"name": "Two Buttons",
"url": "https://i.imgflip.com/1g8my4.jpg",
"width": 600,
"height": 908,
"box_count": 3
},
{
"id": "112126428",
"name": "Distracted Boyfriend",
"url": "https://i.imgflip.com/1ur9b0.jpg",
"width": 1200,
"height": 800,
"box_count": 3
},
{
"id": "131087935",
"name": "Running Away Balloon",
"url": "https://i.imgflip.com/261o3j.jpg",
"width": 761,
"height": 1024,
"box_count": 5
},
{
"id": "247375501",
"name": "Buff Doge vs. Cheems",
"url": "https://i.imgflip.com/43a45p.png",
"width": 937,
"height": 720,
"box_count": 4
},
{
"id": "124822590",
"name": "Left Exit 12 Off Ramp",
"url": "https://i.imgflip.com/22bdq6.jpg",
"width": 804,
"height": 767,
"box_count": 3
},
{
"id": "129242436",
"name": "Change My Mind",
"url": "https://i.imgflip.com/24y43o.jpg",
"width": 482,
"height": 361,
"box_count": 2
},
{
"id": "217743513",
"name": "UNO Draw 25 Cards",
"url": "https://i.imgflip.com/3lmzyx.jpg",
"width": 500,
"height": 494,
"box_count": 2
}
]
}
}
Remember to remove the last comma and add the brackets “]}}” at the end of the file to make sure it’s a valid JSON file.
Creating a File Reader to Read the Response
MockWebServer can’t read the response from the JSON file directly. For this, you need to create a file reader to read the contents of the JSON file and convert them into a String
.
Start by creating a file in androidTest and naming it FileReader.kt.
Add the following code to FileReader.kt:
package com.company.android.whatsthememe
import androidx.test.platform.app.InstrumentationRegistry
import java.io.IOException
import java.io.InputStreamReader
object FileReader {
fun readStringFromFile(fileName: String): String {
try {
val inputStream = (InstrumentationRegistry.getInstrumentation().targetContext
.applicationContext as MemeTestApp).assets.open(fileName)
val builder = StringBuilder()
val reader = InputStreamReader(inputStream, "UTF-8")
reader.readLines().forEach {
builder.append(it)
}
return builder.toString()
} catch (e: IOException) {
throw e
}
}
}
FileReader
consists of a single method: readStringFromFile()
. This opens the JSON file as an InputStream
and writes its contents in a String
line by line. The final, built-up String
is then returned. You’ll use FileReader
to read from the success_response.json file you created earlier.
Writing the Tests
Now that you’ve fully set up the mock server, you’ll write a few Espresso tests to ensure the server is working as expected. But you must do one last thing before writing the tests.
API calls are asynchronous tasks, so you need a way to tell Espresso to wait for the API calls to complete. You’ll use an idling resource to solve this.
Setting up an Idling Resource
An idling resource represents an asynchronous operation whose results affect subsequent operations in a UI test. You can read more about idling resources in the Espresso documentation.
Open the app-level build.gradle and add the following line below the corresponding // TODO 3
at the end of the file:
androidTestImplementation 'com.jakewharton.espresso:okhttp3-idling-resource:1.0.0'
This adds the Gradle dependency, which you need to have before you can call OkHttp3IdlingResource
.
Now, click Sync Now and let Gradle finish syncing.
Open MainActivityTest.kt and add a member variable for OkHttp3IdlingResource
just below the mockWebServer
one, as follows:
private lateinit var okHttp3IdlingResource: OkHttp3IdlingResource
Next, add the following lines to the beginning of setup()
to set up and register the idling resource just before starting the mockWebServer
:
@Before
fun setup() {
okHttp3IdlingResource = OkHttp3IdlingResource.create(
"okhttp",
OkHttpProvider.getOkHttpClient()
)
IdlingRegistry.getInstance().register(
okHttp3IdlingResource
)
mockWebServer.start(8080)
}
Place the cursor on the words IdlingRegistry
and OkHttp3IdlingResource
, respectively, then press Alt-Enter or Option-Return. Next, choose Import to automatically add the missing import lines and fix the build.
OkHttp3IdlingResource
is a class that the okhttp-idling-resource
library provides. It tells Espresso to wait for the OkHttp
client to finish performing its task. OkHttp
is the networking client used in the project.
OkHttpProvider
is a helper class in the project that gives you access to a static OkHttp
instance.
Every time you register an idling resource, you also have to unregister it. Add the following code at the end of teardown()
, just after shutting down the mockWebServer
:
IdlingRegistry.getInstance().unregister(okHttp3IdlingResource)
For your next step, you’ll test to see what happens when the case is successful.