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.

3.5 (2) · 2 Reviews

Download materials
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

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.

Note: If you’re new to UI testing using Espresso, check out this introduction to Espresso tutorial.

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:

Assets directory location

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.