gpt4 book ai didi

android - RxJava+Retrofit 2单元测试怪异错误

转载 作者:行者123 更新时间:2023-11-30 00:55:12 26 4
gpt4 key购买 nike

我正在使用 MVP 模式创建 Android 应用程序。为此,我正在使用 Retrofit 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;
}
}

被测试的Presenter类

public class AlbumPresenterImpl implements AlbumPresenter {

private AlbumView view;
private MediaType type;
private List<Album> albums;
private MediaService mediaService;

public AlbumPresenterImpl(AlbumView view, MediaService mediaService, MediaType type) {
this.view = view;
this.mediaService = mediaService;
this.type = type;
}

@Override
public void getAlbums() {
Observable<List<Album>> observable;

if (type.equals(MediaType.VIDEO)) {
observable = mediaService.getVideoAlbums();
} else {
observable = mediaService.getPhotoAlbums();
}

observable.doOnSubscribe(view::showProgress)
.doAfterTerminate(view::hideProgress)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(items -> {
albums = items;
view.showAlbums(albums);
}, throwable -> {
view.showError(throwable.getLocalizedMessage());
});
}

@Override
public void onResume() {
if (albums == null) {
getAlbums();
}
}

@Override
public void onDestroy() {

}
}

为什么有时测试不通过?

非常感谢!

=================================

更新

正如@Fred 所写,问题出在调度程序中

public class RxSchedulersOverrideRule implements TestRule {

private final RxJavaSchedulersHook mRxJavaSchedulersHook = new RxJavaSchedulersHook() {
@Override
public Scheduler getIOScheduler() {
return Schedulers.immediate();
}

@Override
public Scheduler getNewThreadScheduler() {
return Schedulers.immediate();
}
};

private final RxAndroidSchedulersHook mRxAndroidSchedulersHook = new RxAndroidSchedulersHook() {
@Override
public Scheduler getMainThreadScheduler() {
return Schedulers.immediate();
}
};

// Hack to get around RxJavaPlugins.reset() not being public
// See https://github.com/ReactiveX/RxJava/issues/2297
// Hopefully the method will be public in new releases of RxAndroid and we can remove the hack.
private void callResetViaReflectionIn(RxJavaPlugins rxJavaPlugins)
throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
Method method = rxJavaPlugins.getClass().getDeclaredMethod("reset");
method.setAccessible(true);
method.invoke(rxJavaPlugins);
}

@Override
public Statement apply(final Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
RxAndroidPlugins.getInstance().reset();
RxAndroidPlugins.getInstance().registerSchedulersHook(mRxAndroidSchedulersHook);
callResetViaReflectionIn(RxJavaPlugins.getInstance());
RxJavaPlugins.getInstance().registerSchedulersHook(mRxJavaSchedulersHook);

base.evaluate();

RxAndroidPlugins.getInstance().reset();
callResetViaReflectionIn(RxJavaPlugins.getInstance());
}
};
}

代码取自Github a link !

在测试类中

@Rule
public final RxSchedulersOverrideRule mOverrideSchedulersRule = new RxSchedulersOverrideRule();

最佳答案

看起来你覆盖了主线程调度器:

RxAndroidPlugins.getInstance().registerSchedulersHook(new RxAndroidSchedulersHook() {
@Override
public Scheduler getMainThreadScheduler() {
return Schedulers.immediate();
}
});

但是从代码来看,observables 仍然在 Schedulers.io() 调度器上运行:

observable.doOnSubscribe(view::showProgress)
.doAfterTerminate(view::hideProgress)
.subscribeOn(Schedulers.io())
// ...

您可能知道,即时调度程序在当前线程中执行代码,我猜这是因为您跳转到了 io 调度程序,它与运行测试的调度程序不同。

这将使测试在一个线程中运行,而订阅者/可观察对象在另一个线程中运行。这可以解释为什么有时测试通过而有时没有通过。存在竞争条件。

基本最简单的方法是确保在测试时您在 Schedulers.immediate() 上同时拥有 observeOnsubscribeOn运行时你有正确的,即 Schedulers.io()AndroidSchedulers.mainThread()

您可以通过覆盖调度程序,将它们作为构造函数传递来实现,或者您甚至可以查看 this Dan Lew 解释了如何使用 compose 创建调度器转换器。然后,您可以确保您的类在运行时使用适当的调度程序转换器,并在测试时使用一些转换器将所有内容放在即时线程上。

关于android - RxJava+Retrofit 2单元测试怪异错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40281545/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com