Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 998
vkislicins Avatar asked Mar 31 '15 17:03

vkislicins


1 Answers

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

like image 136
Eugen Martynov Avatar answered Nov 01 '22 02:11

Eugen Martynov