简体   繁体   中英

Android Unit Test with Retrofit and Mockito

I separated retrofit api calls methods from the activity code and I want to do a unit test on these methods, one example: The interface:

public interface LoginService {
    @GET("/auth")
    public void basicLogin(Callback<AuthObject> response);
}

and this is the method that do the call, in the main activity I get the object by the event bus.

public class AuthAPI {
    private Bus bus;
    LoginService loginService;

    public AuthAPI(String username, String password) {
        this.bus = BusProvider.getInstance().getBus();
        loginService = ServiceGenerator.createService(LoginService.class,
                CommonUtils.BASE_URL,
                username,
                password);
    }

    public void Login() {

        loginService.basicLogin(new Callback<AuthObject>() {
            @Override
            public void success(AuthObject authObject, Response response) {
                bus.post(authObject);
            }

            @Override
            public void failure(RetrofitError error) {
                AuthObject authObject = new AuthObject();
                authObject.setError(true);
                bus.post(authObject);
            }
        });
    }

}

And here the test

@RunWith(MockitoJUnitRunner.class)
public class AuthCallTest extends TestCase {

    AuthAPI authAPI;

    @Mock
    private LoginService mockApi;

    @Captor
    private ArgumentCaptor<Callback<AuthObject>> cb;

    @Before
    public void setUp() throws Exception {
        authAPI = new AuthAPI("username", "password");
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testLogin() throws Exception {

        Mockito.verify(mockApi).basicLogin((cb.capture()));

        AuthObject authObject = new AuthObject();
        cb.getValue().success(authObject, null);

        assertEquals(authObject.isError(), false);
    }
}

when I launch the test I have this error

Wanted but not invoked:
mockApi.basicLogin(<Capturing argument>);
-> at AuthCallTest.testLogin(AuthCallTest.java:42)
Actually, there were zero interactions with this mock.

What I did wrong, this is driving me crazy I tried to follow this guide without success: http://www.mdswanson.com/blog/2013/12/16/reliable-android-http-testing-with-retrofit-and-mockito.html

someone help me :(

The article isn't very clear as it misses out the setup steps. By visiting the GitHub project linked in the article, you can see the full source code which explains those missing steps:

1) The code samples are extracted from a test class testing a specific activity. As part of the setup (ie in @Before ), it replaces the Activity's reference to a GitHub API implementation with a mock one. It then calls the Activity's onCreate() .

2) During onCreate() , the activity makes a call to the now-replaced GitHub API, passing in its Callback object.

Those first two steps explain why the Mockito.verify(mockApi).repositories(Mockito.anyString(), cb.capture()); step at the beginning of each test works. As the test is run after @Before , the mockApi has indeed had a call on its repositories() method.

The rest of the code is easier to understand once that's in place. As he's only created a mockApi , but not changed the actual Callback being used, the activity's content is changed. The rest of the code then verifies that those changes have taken place, either by checking a ListView or the Toasts.


So to answer your question, you need to:

1) At the start of your test method, replace the AuthAPI's loginService object with your mockApi object, then call AuthAPI.Login() .

2) Use verify() as you already are to check that the function has been called.

3) Create a sample AuthObject and pass it to the cb.getValue().success() function.

4) Obtain the AuthObject from your Bus and assert that it is the same one you sent to the callback.success() function.

This tests that your AuthAPI.Login() correctly sends to your Bus the AuthObject that it would retrieve from Retrofit.


(I realise the SO question was written some time ago, but as I came across the same article and had the same confusion very recently, I thought this answer could be useful for others.)

The problem is that you call verify at the wrong moment: the purpose of verify is to verify that the interactions with mockApi were what you expected. So normally you would see something like:

authApi.login();
Mockito.verify(mockApi).basicLogin((cb.capture()));

That's also what the error message is telling you: verify expected basicLogin to be called but it wasn't.

I've read that article too and felt there was something missing. I don't actually undestand argument capture yet. So can't help you with that :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM