Question

I'm looking for a way to mock api responses in android tests.

I have read the roboelectric could be used for this but I would really appreciate any advice on this.

Was it helpful?

Solution

After a small bit of looking around on the web I have found MockWebServer to be what I was looking for.

A scriptable web server for testing HTTP clients. This library makes it easy to test that your app Does The Right Thing when it makes HTTP and HTTPS calls. It lets you specify which responses to return and then verify that requests were made as expected.

To get setup just add the following to your build.gradle file.

androidTestCompile 'com.google.mockwebserver:mockwebserver:20130706'

Here is a simple example taking from their GitHub page.

public void test() throws Exception {
    // Create a MockWebServer. These are lean enough that you can create a new
    // instance for every unit test.
    MockWebServer server = new MockWebServer();

    // Schedule some responses.
    server.enqueue(new MockResponse().setBody("hello, world!"));

    // Start the server.
    server.play();

    // Ask the server for its URL. You'll need this to make HTTP requests.
    URL baseUrl = server.getUrl("/v1/chat/");

    // Exercise your application code, which should make those HTTP requests.
    // Responses are returned in the same order that they are enqueued.
    Chat chat = new Chat(baseUrl);

    chat.loadMore();
    assertEquals("hello, world!", chat.messages());

    // Shut down the server. Instances cannot be reused.
    server.shutdown();
  }

Hope this helps.

OTHER TIPS

MockWebServer didn't work for me with AndroidTestCase. For instance, ECONNREFUSED error happened quite randomly (described in https://github.com/square/okhttp/issues/1069). I didn't try Robolectric.

As of OkHttp 2.2.0, I found an alternative way which worked well for me: Interceptors. I placed the whole mock response inside a json file stored on androidTest/assets/, say, 'mock_response.json'. When I instanced an OkHttp for testing, I exposed an Interceptor which I would rewrite the incoming response. Basically, body() would instead stream the data in 'mock_response.json'.

public class FooApiTest extends AndroidTestCase {
    public void testFetchData() throws InterruptedException, IOException {
        // mock_response.json is placed on 'androidTest/assets/'
        final InputStream stream = getContext().getAssets().open("mock_response.json");

        OkHttpClient httpClient = new OkHttpClient();
        httpClient.interceptors().add(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                return new Response.Builder()
                        .protocol(Protocol.HTTP_2)
                        // This is essential as it makes response.isSuccessful() returning true.
                        .code(200)
                        .request(chain.request())
                        .body(new ResponseBody() {
                            @Override
                            public MediaType contentType() {
                                return null;
                            }

                            @Override
                            public long contentLength() {
                                // Means we don't know the length beforehand.
                                return -1;
                            }

                            @Override
                            public BufferedSource source() {
                                try {
                                    return new Buffer().readFrom(stream);
                                } catch (IOException e) {
                                    e.printStackTrace();
                                    return null;
                                }
                            }
                        })
                        .build();
            }
        });

        FooApi api = new FooApi(httpClient);
        api.fetchData();

        // TODO: Let's assert the data here.
    }
}

This is now even easier with Mockinizer which makes working with MockWebServer easier:

val mocks: Map<RequestFilter, MockResponse> = mapOf(

    RequestFilter("/mocked") to MockResponse().apply {
        setResponseCode(200)
        setBody("""{"title": "Banana Mock"}""")
    },

    RequestFilter("/mockedError") to MockResponse().apply {
        setResponseCode(400)
    }

)

Just create a map of RequestFilter and MockResponses and then plug it into your OkHttpClient builder chain:

OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .mockinize(mocks) // <-- just plug in your custom mocks here
            .build()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top