【问题标题】:How to assert if a Completable has been subscribed/completed (RxJava2)如何断言 Completable 是否已被订阅/完成(RxJava2)
【发布时间】:2017-06-01 15:03:30
【问题描述】:

我无法确定在我的测试中是否订阅了 Completable,例如:

interface IWebApi {
    Observable<Data> download();
}

interface IDataRepository {
    Completable insert(Data data);
}

class SyncService {

    IWebApi webApi;
    IDataRepository repository;

    public SyncService(IWebApi webApi, IDataRepository repository) {
        this.webApi = webApi;
        this.repository = repository;
    }

    public Completable sync() {
        return webApi.download()
            .flatMapCompletable((Data data) -> { repository.insert(data) })
    }
}

然后在我的测试中:

@Test
public void syncTest() {
    Data data = new Data();
    IDataRepository repository = mock (IDataRepository.class);
    IWebApi webApi = mock (IWebApi.class);

    given(webApi.download()).willReturn(Observable.just(data));
    given(repository.insert(data)).willReturn(Completable.complete());

    TestObserver<Void> observer = new TestObserver<Void>();
    SyncService service = new SyncService(webApi, repository);
    service.sync()
            .subscribe(observer);

    observer.assertComplete();
    verify(repository).insert(data);
}

此测试将通过。但是我可以在不使用flatMapCompletable 的情况下重写同步方法,如下所示:

    public Completable sync() {
        return webApi.download()
            .doOnNext((Data data) -> { repository.insert(data) })
            .ignoreElements();
    }

然后我的测试将通过,但代码将无法运行,因为即使我认为我已经调用了 insert 方法,我也没有在其中调用 subscribe()

我应该如何处理这个问题?

P.S 我是 RxJava 新手,所以如果我没有使用最佳实践,我很想知道 :)

更新

修复了Maxim Ostrovidov指出的不调用.ingnoreElements()的错误

【问题讨论】:

  • flatMapCompletable 方式没有任何问题。
  • @akarnokd 是的,我认为实际上它更可取,但我相信如果有人出于某种原因将其更改为不订阅 observable 的方法之一,测试应该会失败

标签: java unit-testing mockito rx-java rx-java2


【解决方案1】:

您可以使用test() 操作符来方便:

SyncService service = new SyncService(webApi, repository);
TestObserver observer = service.sync().test();

但我可以在不使用 flatMapCompletable 的情况下重写同步方法,如下所示:

public Completable sync() {
    return webApi.download()
        .doOnNext((Data data) -> { repository.insert(data) })
}

这不会编译,因为doOnNext 仅用于调用对项目的操作,而不是更改流返回类型。在您的情况下,方法期望 Completable 但实际上它将是 Observable&lt;Data&gt;

即使你强制改变最终的流类型:

public Completable sync() {
    return webApi.download()
        .doOnNext((Data data) -> { repository.insert(data) })
        .ignoreElements(); //converts to Completable
}

repository.insert(data) 不会被调用,因为doOnNext 没有订阅您传递的任何内容,也没有返回任何内容:

//under the hood of lambdas
.doOnNext(new Consumer<Data>() {
    @Override
    public void accept(Data data) throws Exception {

    }
})

您的初始代码最适合您想要实现的目标:

public Completable sync() {
    return webApi.download()
        .flatMapCompletable((Data data) -> { repository.insert(data) })
}

flatMapCompletable 使用Observable 发出的项目订阅传递的Completable

.flatMapCompletable(new Function<Data, CompletableSource>() {
    @Override
    public CompletableSource apply(Data data) throws Exception {
        return repository.insert(data);
    }
})

编辑

要测试repository.insert 订阅的事实,您可以使用另一个TestObserverCompletable 并应用doOnSubscribe

TestObserver repositoryInsertObserver = TestObserver.create();
Completable insertCompletable = Completable.complete()
    .doOnSubscribe(d -> repositoryInsertObserver.onSubscribe(d));

//pass completable to your mock
given(repository.insert(data)).willReturn(insertCompletable);

//and verify that subscription took place
repositoryInsertObserver.assertSubscribed();

【讨论】:

  • 感谢您的回答!!我忘了打电话给ignoreElements,已经更新了。我不知道测试操作员,那很好!我同意flatMapCompletable 是最好的方法,但如果有人错误地认为doOnNext 会订阅,我想确保测试中断。您知道任何解决方案吗?
  • 谢谢!!这正是我想要的!
猜你喜欢
  • 1970-01-01
  • 2012-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-09
  • 1970-01-01
相关资源
最近更新 更多