I have the following method in the presenter.
public void addNote(int customerId, String body) {
disposables = RxUtil.initDisposables(disposables);
if (TextUtils.isEmpty(body)) {
view.showNoteTextEmpty();
return;
}
if (customerId == Constants.ZERO) {
view.showNoteError("There is a problem with adding note. Try again!");
return;
}
Disposable disposable = userPrefRepository.getLoggedInUser()
.subscribeOn(Schedulers.io())
.map(user -> getNote(body, user))
.flatMap(note -> customersRepository.addNote(customerId, note))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(response -> {
if (response.isSuccessful()) {
view.onNoteAdded();
} else if (response.code() == 401) {
view.handleUnauthorisedError();
} else {
view.onNoteNotAdded();
}
}, view::handleError);
disposables.add(disposable);
}
Now I want to unit test it with the following class:
@RunWith(PowerMockRunner.class) @PrepareForTest(TextUtils.class)
public class NoteDetailsPresenterTest extends BaseTest {
@Rule TrampolineSchedulerRule trampolineSchedulerRule = new TrampolineSchedulerRule();
@Mock CustomersRepository customersRepository;
@Mock UserRepository userRepository;
@Mock RolesManager rolesManager;
@Mock NoteDetailsPresenter.View view;
private NoteDetailsPresenter presenter;
@Before
public void setUp() {
mockTextUtils();
presenter = new NoteDetailsPresenter(customersRepository, userRepository, rolesManager);
presenter.setView(view);
}
@Test
public void shouldAddNote() {
// Given
User user = User.newBuilder()
.withUserId(1)
.build();
// When
Mockito.when(userRepository.getLoggedInUser()).thenReturn(Single.just(user));
Note note = presenter.getNote("Note body", user);
Response<Note> response = Response.success(200, note);
Mockito.when(customersRepository.addNote(1, note)).thenReturn(Single.just(response));
presenter.addNote(1, "Note body");
// Then
Mockito.verify(view).onNoteAdded();
}
}
But it fails with the following exception:
Wanted but not invoked: view.onNoteAdded(); -> at com.anstar.presentation.notes.NoteDetailsPresenterTest.shouldAddNote(NoteDetailsPresenterTest.java:56)
However, there were other interactions with this mock: view.handleError( java.lang.NullPointerException: The single returned by the mapper is null ); -> at io.reactivex.internal.observers.ConsumerSingleObserver.onError(ConsumerSingleObserver.java:46)
How I can solve it? Is is the problem regarding map and flatMap transformations?
It seems that the mock can't be read. Try to put any()
on the parameters:
Instead of this:
Mockito.when(customersRepository.addNote(1, note)).thenReturn(Single.just(response));
Use any()
:
Mockito.when(customersRepository.addNote(anyInt(), any(Note.class))).thenReturn(Single.just(response));
Why the mock can't read?
If parameters are primitive data types (string, int, double, etc) you can just pass the exact parameter (in your case, the first parameter which is integer, 1) and the mock will be read. However in objects (in your case, Note
object), even though you have the same exact parameters, they will have different hashCode()
so the mock can' t be read. Solution for this is to accept any()
parameter specifying the class type: any(Note.class)
.