如何为RxJava CompositeSubscription编写单元测试

时间:2017-01-18 08:02:27

标签: android unit-testing junit rx-java

@Override public void onBarcodeReceived(final String barcode) {
    view.showProgress();
    if (!textUtil.isEmpty(barcode)) {
      subscriptions.add(
          interactor.getSearchResultByBarcode(barcode).subscribe(subscriberForSearchResults(true)));
    }
  }

  private Subscriber<PriceAndStockActivityViewModel> subscriberForSearchResults(
      boolean fromBarcode) {
    return new BaseSubscriber<PriceAndStockActivityViewModel>() {
      @Override public void onNext(PriceAndStockActivityViewModel priceAndStockActivityViewModel) {
        super.onNext(priceAndStockActivityViewModel);
        view.updateView(priceAndStockActivityViewModel);
      }

      @Override public void onError(Throwable e) {
        super.onError(e);
        view.hideProgress();
        view.organizeScreenComponentsByVisibility(true);
        view.onError(e);
      }
    };
  }

我想测试名为onBarcodeReceived的方法,如下所示

@Test public void should_updateViewByViewModel_when_AnyBarcodeReceived() {
    String barcode = "123123123";
    PriceAndStockActivityViewModel viewModel = getPriceAndStockActivityViewModel(barcode);
    when(textUtil.isEmpty(barcode)).thenReturn(false);
    when(interactor.getSearchResultByBarcode(anyString())).thenReturn(Observable.just(viewModel));

    presenter.onBarcodeReceived(barcode);

    verify(view).showProgress();
    verify(interactor).getSearchResultByBarcode(anyString());
    verify(view).updateView(any(PriceAndStockActivityViewModel.class));
  }

由于onNext在不同的线程中运行,因此通常不会到达view.updateView。它看起来很简单,但我找不到如何解决它。有没有办法验证updateView?

2 个答案:

答案 0 :(得分:1)

我认为getSearchResultByBarcode()适用于后台线程。所以我想知道你如何能够从这个后台线程更改你的UI?

我将subscriber的执行更改为Android的主线程,以便您可以安全地操纵视图,无论将来getSearchResultByBarcode()的线程是否发生变化。但是不会直接对Scheduler进行硬编码,而是将其注入到演示者类中,例如通过构造函数。当然,当你创建“真正的”演示者时,你会传递AndroidSchedulers.mainThread()

public MyPresenter(, Scheduler observeScheduler) {
    ...
    this.observeScheduler = observeScheduler;
}

....

@Override 
public void onBarcodeReceived(final String barcode) {
     view.showProgress();
     if (!textUtil.isEmpty(barcode)) {
          subscriptions.add(interactor.getSearchResultByBarcode(barcode)
                .observeOn(observeScheduler)
                .subscribe(subscriberForSearchResults(true)));
     }
}

然后在您的测试中,在构建Presenter时,您将使用Schedulers.immediate()(如果您正在使用RxJava 1.x或Schedulers.trampoline(),如果您正在使用RxJava 2.x那个应该可以在没有使用Mockito的单元测试中使用任何timeout()的情况下工作......毕竟你希望它们尽可能快地运行。

和一个无关的事情 - 您可以使用org.apache.commons.lang3.StringUtils替换android.text.TextUtils - 它具有大致相同的功能,但您不需要在单元测试中模拟它。

答案 1 :(得分:0)

为了等待另一个线程完成,您可以使用此Mockito功能:verify with timeout

verify(view, timeout(100)).updateView(any(PriceAndStockActivityViewModel.class));

或者使用某些线程同步方法,如CountDownLatch。请参阅Mockito here的示例。