如何在 RxJava flatMap 中测试函数?
How to test function inside RxJava flatMap?
我在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);
}
现在我想使用以下 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();
}
}
但它失败了,出现以下异常:
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)
我该如何解决?是关于地图和平面地图转换的问题吗?
mock好像读不出来。尝试将 any()
放在参数上:
而不是这个:
Mockito.when(customersRepository.addNote(1, note)).thenReturn(Single.just(response));
使用any()
:
Mockito.when(customersRepository.addNote(anyInt(), any(Note.class))).thenReturn(Single.just(response));
为什么mock无法读取?
如果参数是原始数据类型(字符串、整数、双精度等),您只需传递确切的参数(在您的情况下,第一个参数是整数 1),然后将读取模拟。但是在对象中(在您的情况下,Note
对象),即使您具有完全相同的参数,它们也会有不同的 hashCode()
,因此无法读取模拟。解决方案是接受指定 class 类型的 any()
参数:any(Note.class)
.
我在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);
}
现在我想使用以下 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();
}
}
但它失败了,出现以下异常:
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)
我该如何解决?是关于地图和平面地图转换的问题吗?
mock好像读不出来。尝试将 any()
放在参数上:
而不是这个:
Mockito.when(customersRepository.addNote(1, note)).thenReturn(Single.just(response));
使用any()
:
Mockito.when(customersRepository.addNote(anyInt(), any(Note.class))).thenReturn(Single.just(response));
为什么mock无法读取?
如果参数是原始数据类型(字符串、整数、双精度等),您只需传递确切的参数(在您的情况下,第一个参数是整数 1),然后将读取模拟。但是在对象中(在您的情况下,Note
对象),即使您具有完全相同的参数,它们也会有不同的 hashCode()
,因此无法读取模拟。解决方案是接受指定 class 类型的 any()
参数:any(Note.class)
.