1

I'm using Mockito to test my views but my tests are failing because of a method that is supposed to be called after a retrofit call is complete. How can I mock a view who's method is called by presenter after completion of a retrofit call? I'd like to verify that unBlockUI() below has been called. My tests show blockUI() is called but unblockUI() is not being called.

I get a fail message

Wanted but not invoked:
view.unBlockUI();

In my presenter I have the method

public void fetchResults(){ 

    view.blockUI();

    ResultsDataService resultsDataService = new ResultsDataService()

    resultsDataService.getAllResults(new Callback<Catalog>() {

            @Override
            public void onResponse(Call<Catalog> call, Response<Catalog> response) {
                view.unBlockUI();

            }

            @Override
            public void onFailure(Call<Catalog> call, Throwable t) {
                view.unBlockUI();               
                t.printStackTrace();
            }
        })
}

Results data service.

public class ResultsDataService {

    private final RestApi restApi;

    public CatalogDataService() {
    //here I have a class that builds the REST Service
        restApi = RestServiceBuilder.createService(RestApi.class);
    }

    public void getAllResults() {
        Call<Catalog> call = restApi.getAllResults();
        call.enqueue(callback);
    }
}

my test method

@Test
public void shouldFetchAllResults_allOK() {
presenter.fetchResults();`

verify(view).blockUI();//This is called
verify(view).unBlockUI();//this is not called
}
sammyukavi
  • 1,501
  • 2
  • 23
  • 51
  • the way you are calling the retrofit function will be executed in background, so before you get the response from the server, solution is to to call call.execute() which should be in asynctask, i think you are using call.enqueue to call the api – Mohd Asif Ahmed Jun 23 '17 at 05:24
  • But I don't want to execute the call on the main thread – sammyukavi Jun 23 '17 at 06:53
  • Android does not allow any network operation on main thread, So, if you want to call any thing after some network operation, instead of parallel execution, use asyncTask class. replace your retrofit.enqueue() to retrofit.execute(). Now call this retrofit.execute() in your doInBackground() of your asynctask, and in onPostExecute, updateUI or call whatever you want to call after your response – Mohd Asif Ahmed Jun 23 '17 at 07:10

1 Answers1

2

I think one possible solution is to mock ResultsDataService to call the onResponse method of any callback every time getAllResults is called.

Unfortunately, the way you're creating your ResultsDataService inside fetchResults makes it really hard to do this. This is what we call tight coupling. You have a method that depends strictly on ResultsDataService with no chance to change it. Therefore you cannot control the presenter from the outside. As a rule of thumb, every time you see the new operator that's a sign of tight coupling.

Usually we use dependency injection to solve this. One way you can do it in your code is simply change the fetchResults method to receive the service as an argument:

public void fetchResults(@NonNull ResultsDataService service) {
   // ...
}

It might not seem much, but now in the test you can pass in a configured mock and in your app you just pass in the real service.

Say now in your test you'd configure a mock like so:

ResultDataService service = mock(ResultDataService.class);
doAnswer(new Answer() {
        @Override
        public Object answer(InvocationOnMock invocation) throws Throwable {
            Call call = (Call) invocation.getArgument(0);
            call.onResponse(call, <some response here>);
            return null;
        }
    }).when(service.getAllResults(any(Call.class)));    

You can now use this to pass it to your presenter fetchResults.

What does the mock above do? It will call the onResponse method of the passed in argument. So basically it will call right away the onResponse callback when you call fetchResults. In your case this will in turn call unBlockUI.

Notice you can do something similar to test the onFailure. You should also make ResultsDataService an interface, so your presenter doesn't depend on concrete implementations, but just interfaces. This is much more flexible.

Hope this helps. Remember, this is one way of doing this and not the single way.

Fred
  • 16,367
  • 6
  • 50
  • 65