Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/192.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android RxJava+;改造2单元测试错误_Android_Unit Testing_Mockito_Rx Java_Retrofit2 - Fatal编程技术网

Android RxJava+;改造2单元测试错误

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)

我正在使用MVP模式创建Android应用程序。 为此,我使用了改型2和RxJava。应用程序运行良好

但在单元测试中,我遇到了一个奇怪的错误。同一个测试代码有时通过,有时失败

此消息将显示错误

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,我只写一个更完整的答案,这样其他人也能从中受益:)