【问题标题】:Verify interactions in rxjava subscribers验证 rxjava 订阅者中的交互
【发布时间】:2015-07-14 16:10:53
【问题描述】:

以 MVP 模式描绘您的演示者订阅返回观察者的服务的情况:

public void gatherData(){
   service.doSomeMagic()
      .observeOn(Schedulers.io())
      .subscribeOn(AndroidSchedulers.mainThread())
      .subscribe(new TheSubscriber());
}

现在TheSubscriber 类从视图中调用onNext 一个方法,比如:

@Override public void onNext(ReturnValue value) {
  view.displayWhatever(value);
}

现在,在我的单元测试中,我想验证当在非错误情况下调用方法 gatherData() 时,调用视图的方法 displayWhatever(value)

问题

有没有干净的方法来做到这一点?

背景

  • 我正在使用 mockito 来验证交互,当然还有更多
  • Dagger 正在注入除TheSubscriber 之外的整个演示者

我尝试了什么

  • 注入订阅者并在测试中模拟它。对我来说看起来有点脏,因为如果我想更改演示者与服务交互的方式(不是 Rx),那么我需要更改很多测试和代码。
  • 模拟整个服务。这还不错,但需要我模拟很多方法,但我并没有完全达到我想要的效果。
  • 在互联网上四处查找,但似乎没有人有一种干净直接的方法来做到这一点

感谢您的帮助

【问题讨论】:

  • MVP 是一个超载的术语。我认为MVP View也是接口,可以注入Presenter。因此,您可以为应用程序注入 Activity/Fragment 并为测试注入 mockito 模拟。
  • 是的,视图被注入和模拟了。这里的问题是如何轻松配置模拟的service 以调用订阅者的方法,这些方法最终会从视图中调用。
  • 你问的是when(service.doSomeMagic()).thenReturn(Observable.just("yourvalue-with-correct-type"));吗?
  • 有时最简单的解决方案并不那么明显。是的,您的评论实际上有所帮助。不得不做一些额外的事情,但这与我的应用程序有关。要添加答案吗?

标签: android mockito rx-java


【解决方案1】:

假设您以类似的方式使用serviceview 的接口:

class Presenter{
  Service service;
  View view;

  Presenter(Service service){
    this.service = service;
  }

  void bindView(View view){
    this.view = view;
  }

  void gatherData(){
    service.doSomeMagic()
      .observeOn(Schedulers.io())
      .subscribeOn(AndroidSchedulers.mainThread())
      .subscribe(view::displayValue);
  }
}

然后可以提供模拟来控制和验证行为:

@Test void assert_that_displayValue_is_called(){
  Service service = mock(Service.class);
  View view = mock(View.class);
  when(service.doSomeMagic()).thenReturn(Observable.just("myvalue"));
  Presenter presenter = new Presenter(service);
  presenter.bindView(view);

  presenter.gatherData();

  verify(view).displayValue("myvalue");
}

【讨论】:

  • 我几乎已经运行了测试用例。但最终以`引起:java.lang.RuntimeException:android.os.Looper中的方法getMainLooper未模拟。有关详细信息,请参阅g.co/androidstudio/not-mocked。在 android.os.Looper.getMainLooper(Looper.java) 在 io.reactivex.android.schedulers.AndroidSchedulers.(AndroidSchedulers.java:24) ... 32 更多`。我检查了这个github.com/ReactiveX/RxAndroid/issues/238,但我仍然没有办法测试 RxJava 代码
  • 这与我的想法几乎相同,但我一直在 Schedulers.io() 行上遇到 NullPointerException。然后在 subscribe() 行上,如果我注释掉我正在使用的调度程序 - 我使用匿名内部类/lambda 订阅。
  • 我最终也注入了调度程序。在测试时,我同时使用 observeOnsubscribeOn Schedulers.immediate()
【解决方案2】:

我知道它已经很晚了,但它可能对某人有所帮助,因为我搜索了很长时间来寻找您问题的解决方案:D

对我来说,添加Observable.Transformer<T, T> 如下:

    void gatherData() {
        service.doSomeMagic()
          .compose(getSchedulerTransformer())
          .subscribe(view::displayValue);
    }

    private <T> Observable.Transformer<T, T> getSchedulerTransformer() {
        if (mTransformer == null) {
            mTransformer = (Observable.Transformer<T, T>) observable -> observable.subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread());
        }

        return mTransformer;
    }

    void setSchedulerTransformer(Observable.Transformer<Observable<?>, Observable<?>> transformer) {
        mTransformer = transformer;
    }

为了设置 Transformer,我刚刚通过了这个:

setSchedulerTransformer(observable -> {
            if (observable instanceof Observable) {
                Observable observable1 = (Observable) observable;
                return observable1.subscribeOn(Schedulers.immediate())
                        .observeOn(Schedulers.immediate());
            }
            return null;
        });

所以只需在您的测试中添加一个@Before 方法并调用presenter.setSchedulerTransformer,它应该能够测试这个:)

希望这会有所帮助并且可以理解:D

【讨论】:

  • 哇,迟到总比不到好! :D 是的,您的解决方案非常酷。我现在自己做。基本上快进几年和更多的经验,答案归结为:注入你的调度程序!
  • :D 没问题...我因为这个问题而大发雷霆,现在它解决了,测试是绿色的。祝你未来好运:)
猜你喜欢
  • 1970-01-01
  • 2016-03-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多