我有一个API接口,我正在测试一个涉及网络调用的View
。
@Config(emulateSdk = 18)
public class SampleViewTest extends RobolectricTestBase {
ServiceApi apiMock;
@Inject
SampleView fixture;
@Override
public void setUp() {
super.setUp(); //injection is performed in super
apiMock = mock(ServiceApi.class);
fixture = new SampleView(activity);
fixture.setApi(apiMock);
}
@Test
public void testSampleViewCallback() {
when(apiMock.requestA()).thenReturn(Observable.from(new ResponseA());
when(apiMock.requestB()).thenReturn(Observable.from(new ResponseB());
AtomicReference<Object> testResult = new AtomicReference<>();
fixture.updateView(new Callback() {
@Override
public void onSuccess(Object result) {
testResult.set(result);
}
@Override
public void onError(Throwable error) {
throw new RuntimeException(error);
}
});
verify(apiMock, times(1)).requestA();
verify(apiMock, times(1)).requestB();
assertNotNull(testResult.get());
}
}
由于某些原因,apiMock
方法永远不会被调用,验证总是失败。
在我看来,我是这样调用api的
apiV2.requestA()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer());
我在这里错过了什么?
Update # 1:
经过一些调查,在我的实现(上面的示例)中,似乎没有调用observeOn(AndroidSchedulers.mainThread())
订阅者。还是不知道为什么。
更新# 2:
当订阅时,就像apiV2.requestA().subscribe(new Observer());
一样,一切都工作得很好——mock api被调用,测试通过。
推进ShadowLooper.idleMainLooper(5000)
没有任何作用。甚至在HandlerThreadScheduler
中从处理程序中抓取了looper并推进了它。相同的结果。
更新# 3:添加使用API的实际代码。
public void updateView(final Callback) {
Observable.zip(wrapObservable(api.requestA()), wrapObservable(api.requestB()),
new Func2<ResponseA, ResponseB, Object>() {
@Override
public Object call(ResponseA responseA, ResponseB responseB) {
return mergeBothResponses(responseA, responseB);
}
}
).subscribe(new EndlessObserver<Object>() {
@Override
public void onError(Throwable e) {
Log.e(e);
listener.onError(e);
}
@Override
public void onNext(Object config) {
Log.d("Configuration updated [%s]", config.toString());
listener.onSuccess(config);
}
});
}
protected <T> Observable<T> wrapObservable(Observable<T> observable) {
return observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
}
我仍然在思考如何正确使用rxjava,但我会尝试修改你的代码,以便你只在最终压缩的可观察对象上观察到(mainThread),而不是在原始请求响应的可观察对象上做它。然后我将验证这是否会影响你必须推进两个循环器的事实。
为了简化您的测试并消除对loop空闲的需要,我将把线程从等式中去掉,因为您在运行测试时不需要后台处理。可以通过注入调度器而不是静态地创建调度器来实现这一点。当运行你的产品代码时,你会有AndroidSchedulers。主线程和调度程序。当运行测试代码时,您将注入调度器。立即执行
@Inject
@UIScheduler /* Inject Schedulers.immediate for tests and AndroidSchedulers.mainThread for production code */
private Scheduler mainThreadSched;
@Inject
@IOScheduler /* Inject Scheduler.immediate for tests and Schedulers.io for production code */
private Scheduler ioSched;
public void updateView(final Callback) {
Observable.zip(wrapObservable(api.requestA()), wrapObservable(api.requestB()),
new Func2<ResponseA, ResponseB, Object>() {
@Override
public Object call(ResponseA responseA, ResponseB responseB) {
return mergeBothResponses(responseA, responseB);
}
}
).observeOn(mainThreadSched)
.subscribe(new EndlessObserver<Object>() {
@Override
public void onError(Throwable e) {
Log.e(e);
listener.onError(e);
}
@Override
public void onNext(Object config) {
Log.d("Configuration updated [%s]", config.toString());
listener.onSuccess(config);
}
});
}
protected <T> Observable<T> wrapObservable(Observable<T> observable) {
return observable.subscribeOn(ioSched);
}
您使用的是哪个版本的rxjava ?我知道在0.18中有一些变化。*关于ExecutorScheduler的版本。我有一个类似的问题,当你使用0.18.3时,我不会得到onComplete消息,因为我的订阅会提前取消订阅。我提到这个的唯一原因是0.19.0的修复修复了我的问题。
不幸的是,我不能真正解释什么是固定的细节,这超出了我的理解在这一点上,但如果它被证明是相同的原因也许有人更了解可以解释。这是我正在谈论的内容的链接https://github.com/Netflix/RxJava/issues/1219。
这不是一个很好的答案,但更多的是一个提示,如果它可以帮助你。
正如@champ016所述,RxJava版本低于0.19.0
存在问题。
当使用0.19.0
时,以下方法有效。尽管我还是不太明白为什么我必须推进两个循环。
@Test
public void testSampleViewCallback() {
when(apiMock.requestA()).thenReturn(Observable.from(new ResponseA());
when(apiMock.requestB()).thenReturn(Observable.from(new ResponseB());
AtomicReference<Object> testResult = new AtomicReference<>();
fixture.updateView(new Callback() {
@Override
public void onSuccess(Object result) {
testResult.set(result);
}
@Override
public void onError(Throwable error) {
throw new RuntimeException(error);
}
});
ShadowLooper.idleMainLooper(5000);
Robolectric.shadowOf(
Reflection.field("handler")
.ofType(Handler.class)
.in(AndroidSchedulers.mainThread())
.get().getLooper())
.idle(5000);
verify(apiMock, times(1)).requestA();
verify(apiMock, times(1)).requestB();
assertNotNull(testResult.get());
}