Reputation:
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 :(
Upvotes: 14
Views: 13496
Reputation: 55744
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 (i.e. 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.)
Upvotes: 21
Reputation: 460
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 :)
Upvotes: 1