Android test method called after presenter executes retrofit call

大憨熊 提交于 2019-12-11 04:39:19

问题


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
}

回答1:


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.



来源:https://stackoverflow.com/questions/44713462/android-test-method-called-after-presenter-executes-retrofit-call

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!