vkislicins
vkislicins

Reputation: 3391

Testing okHttp requests with Robolectric - callbacks

I have a function that I want to test which runs in an okHttp callback. I'm trying to test it using Robolectrics but the callback is never executed. I presume that is because the test moves on after request without waiting for okHttp to return. So far I've tried:

    ShadowLooper.pauseMainLooper();
    Robolectric.flushBackgroundScheduler();
    ShadowLooper.unPauseMainLooper();

but that didn't work. Any suggestions?

EDIT:

Here's an example of my code:

ApiClient.sendSomeDataToServer(data, callback);

Where ApiClient is a helper class containing okHttp client. sendSomeDataToServer API call looks something like this:

public static void sendSomeDataToServer(MyObject data, Callback callback){
    final Request request = new Request.Builder()
            .url(API_SOME_URL)
            .post(RequestBody.create(JSON, myObject.getAsJson().toString()))
            .build();
    sHttpClient.newCall(request).enqueue(callback);
}

Where sHttpClient is an initialised OkHttpClient.

I can test the execution of above by forcing Thread.sleep(5000) inside my test code and providing custom callback. The code I'm trying to test is inside the callback. Any suggestions how I can test that? I really don't want to change the main code to fit the test framework - should be the other way round.

Upvotes: 5

Views: 3722

Answers (2)

Nhat Dinh
Nhat Dinh

Reputation: 3447

You can use ArgumentCaptor (Mockito's class). Reference: http://www.mdswanson.com/blog/2013/12/16/reliable-android-http-testing-with-retrofit-and-mockito.html

Upvotes: 0

Eugen Martynov
Eugen Martynov

Reputation: 20140

Lets assume you have next code. Interface:

@GET("/user/{id}/photo")  
void listUsers(@Path("id") int id, Callback<Photo> cb);

Implementation:

public void fetchData() {
    RestAdapter restAdapter = new RestAdapter.Builder()
                .setServer("baseURL")     
                .build();
    ClientInterface service = restAdapter.create(ClientInterface.class);

    Callback<Photo> callback = new Callback<Photo>() {
        @Override
        public void success(Photo o, Response response) {

        }

        @Override
        public void failure(RetrofitError retrofitError) {

        }
    };
    service.listUsers(435, callback);
}

First of all you need to change service instantiation to service injection (as parameter or field). I will do it as parameter:

public void fetchData(ClientInterface clients) {
}

After this text is quite trivial:

@Test
public void checkThatServiceSuccessIsProcessed() {
    ClientInterface mockedClients = mock(ClientInterface.class);

    activity.fetchData(mockedClients);

    // get callback
    ArgumentCaptor<Callback<Photo>> captor = (ArgumentCaptor<Callback<Photo>>)ArgumentCaptor.forClass(Callback.class);
    verify(mockedInterface).listUsers(anything(), captor.capture());
    Callback<Photo> passedCallback = captor.value();
    // run callback
    callback.success(...);
    // check your conditions
}

The used library for mocking and verifying is Mockito.

There will be one warning with captor instantiation because of generics but it fixable if you will use @Captor annotation instead of creating captor by hands.

The parameter injection is not perfect, especially for case of activities. This was used to simplify example. Consider for proper injection with library or without. I would encourage you to try Dagger for injections

Upvotes: 7

Related Questions