【问题标题】:Mock retrofit Observable error模拟改造可观察到的错误
【发布时间】:2017-08-10 11:28:00
【问题描述】:

我正在使用 Mockito 来测试 Presenter 功能。我已经构建了一些 Fake API 响应并且我已经成功地测试了它们。

但是当我想从 Observable 调用 OnError 时,它没有被调用。

我在一个名为 ApiCalls.java 的类中有这个函数:

public Observable<List<Person>> getPeople(String location)
{
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    APIService service = retrofit.create(APIService .class);

    return service.getPeople(location);
}

Presenter中的功能如下:

public void getPeople(final String location) {

    Observable observable= apiCalls.getPeople(location);

     observableResult.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<List<Person>>() {
                @Override
                public void onCompleted() {

                }

                @Override
                public void onError(Throwable e) {
                    Log.w("ON ERROR",e.getMessage());
                    view.showError("An error has occurred");

                }

                @Override
                public void onNext(List<Person> people) {
                    view.refreshPeople(people);
                }
            });
}

然后,在单元测试中,我尝试这样做:

   when(apiCalls.getPeople("London"))
            .thenReturn(Observable.error(new HttpException(buildResponse())));

   presenter.getPeople("London");

   verify(peopleView).showError(Mockito.anyString());

还有 buildResponse:

public Response buildResponse()
{
    String json = getJSONError();

    Response response = Response.error(403, ResponseBody.create(MediaType.parse("application/json") ,json));
    return response;
}

但是,当我测试时,出现以下消息错误:

Wanted but not invoked:
peopleView.showError(<any string>);
-> at com.lordwater.myapp.PeoplePresenterImpl.testError(PeoplePresenterTest.java:154)

编辑:在设置中,我将调度程序设置为中间:

@Before
public void setUp() {

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

    presenter = new PeoplePresenterImpl(peopleView);
}

@After
public void tearDown() {
    RxAndroidPlugins.getInstance().reset();
}

【问题讨论】:

  • 您是否在测试设置中使用任何操作来将调度程序更改为即时?如果不是,则可能是线程问题。
  • @Jahnold 我认为这就是问题所在; .observeOn 可能会切换调度程序,并且测试线程不会等待。
  • @TassosBassoukos 在我的版本中,在 SetUp 中,我将 MainThreadScheduler 设置为 Inmediate。如果问题仍然是.observeOn,我怎样才能让测试线程等待观察者?
  • 好的,测试肯定不会等待可观察的完成。我放了一个 Thread.sleep ,然后它就起作用了。但现在我想知道我该怎么做才能等待可观察对象完成而不是 Thread.sleep。我试图将“.toBlocking”放在“when”的可观察对象中,但编译器说“无法解析方法 thenReturn with BlockingObservable”)

标签: android unit-testing mockito rx-java retrofit2


【解决方案1】:

您的问题是您没有在 RxJavaPlugins 钩子中覆盖 Schdeulers.io(),而只是 mainThread 调度程序:

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

正如您所说,在测试真正的异步调用时,您必须等待结果,否则测试将没有结果,但网络操作本身仍然发生在单独的线程上,因此不会阻塞测试线程,并且测试结束但没有结果。

关于toBlocking()toBlocking() 在您想观察结果时很有用 (onNext()),这会将您的Obesrvable 转换为BlockingObservable,这与内存收集中的行为有些相似,这意味着您需要致电:

 apiCalls.getPeople("London")
    .toBlocking()
    .first();

这将阻塞直到有答案,然后你可以assertEquals这个结果。
但是您的情况在这里有所不同,您想验证订阅者是否按预期使用onError(),这意味着您也在测试订阅。

【讨论】:

  • 感谢您的解释,现在我更好地理解了它是如何工作的。我已经尝试了您的解决方案,并且确实按我的预期工作。
猜你喜欢
  • 2016-09-05
  • 2015-06-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-11
  • 2017-04-06
  • 1970-01-01
  • 2018-11-18
  • 1970-01-01
相关资源
最近更新 更多