Android RxJava+;改造2单元测试错误
我正在使用MVP模式创建Android应用程序。 为此,我使用了改型2和RxJava。应用程序运行良好 但在单元测试中,我遇到了一个奇怪的错误。同一个测试代码有时通过,有时失败 此消息将显示错误Android RxJava+;改造2单元测试错误,android,unit-testing,mockito,rx-java,retrofit2,Android,Unit Testing,Mockito,Rx Java,Retrofit2,我正在使用MVP模式创建Android应用程序。 为此,我使用了改型2和RxJava。应用程序运行良好 但在单元测试中,我遇到了一个奇怪的错误。同一个测试代码有时通过,有时失败 此消息将显示错误 Wanted but not invoked: albumView.showProgress(); -> at kz.afckairat.kairat.media.AlbumPresenterTest.checkGetPhotoAlbums(AlbumPresenterTest.java:66)
Wanted but not invoked:
albumView.showProgress();
-> at kz.afckairat.kairat.media.AlbumPresenterTest.checkGetPhotoAlbums(AlbumPresenterTest.java:66)
Actually, there were zero interactions with this mock.
测试班
public class AlbumPresenterTest {
enter code here
private MediaService mediaService;
private AlbumView albumView;
private AlbumPresenterImpl photoAlbumPresenter;
@Before
public void setUp() throws Exception {
albumView = mock(AlbumView.class);
mediaService = mock(MediaService.class);
photoAlbumPresenter = new AlbumPresenterImpl(albumView, mediaService, MediaType.PHOTO);
RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() {
@Override
public Scheduler getMainThreadScheduler() {
return Schedulers.immediate();
}
});
}
@After
public void tearDown() {
RxAndroidPlugins.getInstance().reset();
}
@Test
public void checkGetPhotoAlbums() {
List<Album> albums = getAlbumList();
when(mediaService.getPhotoAlbums()).thenReturn(Observable.just(albums));
photoAlbumPresenter.getAlbums();
verify(albumView).showProgress();
verify(albumView).showAlbums(albums);
verify(albumView).hideProgress();
}
@Test
public void checkGetPhotoAlbumError() {
String msg = "Error";
when(mediaService.getPhotoAlbums()).thenReturn(Observable.error(new IOException(msg)));
photoAlbumPresenter.getAlbums();
verify(albumView).showProgress();
verify(albumView).showError(msg);
verify(albumView).hideProgress();
}
private List<Album> getAlbumList() {
List<Album> albums = new ArrayList<>();
Album album = new Album(1, "Test1", "test1.jpg", "01.01.2016", 2);
albums.add(album);
album = new Album(2, "Test2", "test2.jpg", "01.01.2016", 2);
albums.add(album);
return albums;
}
}
@Rule
public final RxSchedulersOverrideRule mOverrideSchedulersRule = new RxSchedulersOverrideRule();
}
代码取自Github
在测试课上
public class AlbumPresenterTest {
enter code here
private MediaService mediaService;
private AlbumView albumView;
private AlbumPresenterImpl photoAlbumPresenter;
@Before
public void setUp() throws Exception {
albumView = mock(AlbumView.class);
mediaService = mock(MediaService.class);
photoAlbumPresenter = new AlbumPresenterImpl(albumView, mediaService, MediaType.PHOTO);
RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() {
@Override
public Scheduler getMainThreadScheduler() {
return Schedulers.immediate();
}
});
}
@After
public void tearDown() {
RxAndroidPlugins.getInstance().reset();
}
@Test
public void checkGetPhotoAlbums() {
List<Album> albums = getAlbumList();
when(mediaService.getPhotoAlbums()).thenReturn(Observable.just(albums));
photoAlbumPresenter.getAlbums();
verify(albumView).showProgress();
verify(albumView).showAlbums(albums);
verify(albumView).hideProgress();
}
@Test
public void checkGetPhotoAlbumError() {
String msg = "Error";
when(mediaService.getPhotoAlbums()).thenReturn(Observable.error(new IOException(msg)));
photoAlbumPresenter.getAlbums();
verify(albumView).showProgress();
verify(albumView).showError(msg);
verify(albumView).hideProgress();
}
private List<Album> getAlbumList() {
List<Album> albums = new ArrayList<>();
Album album = new Album(1, "Test1", "test1.jpg", "01.01.2016", 2);
albums.add(album);
album = new Album(2, "Test2", "test2.jpg", "01.01.2016", 2);
albums.add(album);
return albums;
}
}
@Rule
public final RxSchedulersOverrideRule mOverrideSchedulersRule = new RxSchedulersOverrideRule();
您似乎使用以下命令覆盖主线程调度程序:
RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() {
@Override
public Scheduler getMainThreadScheduler() {
return Schedulers.immediate();
}
});
但是从代码来看,可观察对象仍然在Schedulers.io()
scheduler上运行:
observable.doOnSubscribe(view::showProgress)
.doAfterTerminate(view::hideProgress)
.subscribeOn(Schedulers.io())
// ...
正如您可能知道的,即时调度器在当前线程中执行代码,我想这是因为您跳转到io
调度器,它与测试运行的调度器不同
这将使测试在一个线程中运行,而订阅者/可观察者在另一个线程中运行。这就解释了为什么有时考试通过,有时考试不通过。这是一个比赛条件
最简单的方法是确保在测试时,您在调度程序.immediate()
上同时拥有observeOn
和subscribeOn
,并且在运行时拥有正确的调度程序.io()和AndroidSchedulers.mainThread()
您可以通过覆盖调度程序、将它们作为构造函数传递来实现这一点,或者您甚至可以查看Dan Lew在何处解释如何使用
compose
创建调度程序转换器。然后,您可以确保您的类在运行时使用适当的调度程序转换器,在测试时使用一些将所有内容都放在立即线程上的转换器。听起来好像您覆盖了主线程调度以返回立即,但您没有覆盖调度程序.io()
。因此,如果我认为这是正确的,测试在一个线程中运行,演示者订阅者在另一个线程中同时运行,从而导致竞争条件在某些时候如您所期望的那样工作。为了便于测试,您可以尝试在任何地方使用即时调度程序。非常感谢!你是对的。问题出在SchedulerOK,我只写一个更完整的答案,这样其他人也能从中受益:)