2

I'm trying to use mockito on android. I want to use it with some callback. Here my test :

public class LoginPresenterTest {


private User mUser = new User();

@Mock
private UsersRepository mUsersRepository;

@Mock
private LoginContract.View mLoginView;

/**
 * {@link ArgumentCaptor} is a powerful Mockito API to capture argument values and use them to
 * perform further actions or assertions on them.
 */
@Captor
private ArgumentCaptor<LoginUserCallback> mLoadLoginUserCallbackCaptor;

private LoginPresenter mLoginPresenter;

@Before
public void setupNotesPresenter() {
    // Mockito has a very convenient way to inject mocks by using the @Mock annotation. To
    // inject the mocks in the test the initMocks method needs to be called.
    MockitoAnnotations.initMocks(this);

    // Get a reference to the class under test
    mLoginPresenter = new LoginPresenter(mUsersRepository, mLoginView);

    // fixtures
    mUser.setFirstName("Von");
    mUser.setLastName("Miller");
    mUser.setUsername("von.miller@broncos.us");
    mUser.setPassword("Broncos50superBowlWinners");
}

@Test
public void onLoginFail_ShowFail() {

    // When try to login
    mLoginPresenter.login("von.miller@broncos.us", "notGoodPassword");


    // Callback is captured and invoked with stubbed user
    verify(mUsersRepository).login(eq(new User()), mLoadLoginUserCallbackCaptor.capture());
    mLoadLoginUserCallbackCaptor.getValue().onLoginComplete(eq(mUser));

    // The login progress is show
    verify(mLoginView).showLoginFailed(anyString());
}

But I got this error :

Argument(s) are different! Wanted:
mUsersRepository.login(
    ch.example.project.Model.User@a45f686,
    <Capturing argument>
);
-> at example.ch.project.Login.LoginPresenterTest.onLoginFail_ShowFail(LoginPresenterTest.java:94)
Actual invocation has different arguments:
mUsersRepository.login(
    ch.example.project.Model.User@773bdcae,
    ch.example.project.Login.LoginPresenter$1@1844b009
);

Maybe the issue is that the second actual argument is ch.example.project.Login.LoginPresenter$1@1844b009 ?

I followed : https://codelabs.developers.google.com/codelabs/android-testing/#5

Thank you for help =)

Edit

The method I try to test (LoginPresenter):

@Override
public void login(String email, String password) {

    mLoginView.showLoginInProgress();

    User user = new User();
    user.setUsername(email);
    user.setPassword(password);

    mUsersRepository.login(user, new UsersRepository.LoginUserCallback() {

        @Override
        public void onLoginComplete(User loggedUser) {
            mLoginView.showLoginComplete();
        }

        @Override
        public void onErrorAtAttempt(String message) {
            mLoginView.showLoginFailed(message);
        }
    });

}
Xero
  • 3,951
  • 4
  • 41
  • 73

2 Answers2

7
eq(new User())

When using eq (or not using matchers at all), Mockito compares arguments using the equals method of the instance passed in. Unless you've defined a flexible equals implementation for your User object, this is very likely to fail.

Consider using isA(User.class), which will simply verify that the object instanceof User, or any() or anyObject() to skip matching the first parameter entirely.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • Thank you for your answer, but when I delete eq() before my User, I got a new error :" org.mockito.exceptions.misusing.InvalidUseOfMatchersException: Invalid use of argument matchers! 2 matchers expected, 1 recorded: -> at .Login.LoginPresenterTest.onLoginFail_ShowFail(LoginPresenterTest.java:94) This exception may occur if matchers are combined with raw values: //incorrect: someMethod(anyObject(), "raw String"); When using matchers, all arguments have to be provided by matchers. For example: //correct: someMethod(anyObject(), eq("String by matcher")); – Xero Feb 16 '16 at 20:54
  • @Xero: When you use Matchers, you need to use one Matcher (or Captor) per argument, for [implementation-specific reasons](http://stackoverflow.com/q/22822512/1426891). That's why I'm not suggesting removing just the `eq`, but instead replacing the expression with one of the three Matchers I listed (`isA`, `any`, or `anyObject`). – Jeff Bowman Feb 16 '16 at 21:06
  • I tried isA, any and anyObject on User, but I got the same error. You don't think that the error is from the second parameter ? – Xero Feb 16 '16 at 21:12
  • I edited my post with the method I'm trying to test, It could help – Xero Feb 16 '16 at 21:16
  • 2
    I got your code running on my machine, reproduced the problem and applied Jeff fix - all works. You have another error in the code, but rest is fine. Are you sure this line: `verify(mUsersRepository).login(isA(User.class), mLoadLoginUserCallbackCaptor.capture());` is still failing? Also you probably want to change your last verify to: `mLoadLoginUserCallbackCaptor.getValue().onErrorAtAttempt("error");` – Kotse Feb 17 '16 at 09:49
  • Oh good, thank you, it was that. I understand more this tests now. Thank you ! – Xero Feb 17 '16 at 19:18
0

I am using mvp pattern with rxjava 2 and dagger 2, and was stuck on unit testing a presenter using Mockito. The code that gave me the "Argument(s) are different!” Error:

@Mock
ImageService imageService;

@Mock
MetadataResponse metadataResponse;

private String imageId = "123456789";

@Test
public void getImageMetadata() {
    when(imageService.getImageMetadata(imageId)).thenReturn(Observable.just(Response.success(metadataResponse)));

    presenter.getImageMetaData(imageId);
    verify(view).showImageData(new ImageData()));
}

Which throws error messages such as the following:

Argument(s) are different! Wanted: Actual invocation has different arguments: com.example.model.ImageData@5q3v861

Thanks to the answer from @Jeff Bowman, it worked after I changed this line

verify(view).showImageData(new ImageData()));

with

verify(view).showImageData(isA(ImageData.class));
s-hunter
  • 24,172
  • 16
  • 88
  • 130