OnSmpleted时,TestScheduler创建的ColdObserver无法取消订阅?

时间:2013-12-03 12:33:31

标签: c# unit-testing system.reactive

我想检查一下我创建的IObservable是否尊重&#34的礼貌;一旦我完成,我将取消订阅" 。乍一看,我的代码看起来有问题。但是,除了我的代码,并且只使用TestScheduler提供的Observable和Observer,它看起来像是'取消订阅'永远不会发生:

using Microsoft.Reactive.Testing;
using System.Reactive;
...
var ts = new TestScheduler();
var ob = ts.CreateObserver<int>();
var xs = ts.CreateColdObservable<int>(
    new Recorded<Notification<int>>(1, Notification.CreateOnCompleted<int>())
    );

xs.Subscribe(ob);
ts.AdvanceTo(2);
Assert.Equal(1, xs.Subscriptions.Single().Unsubscribe); //<-- Xunit no like

我最初怀疑是观察者,但我尝试使用找到here代码的变体,并且它有效,所以我现在认为SubscribeColdObservable上的实现{1}}行为不正确。

1 个答案:

答案 0 :(得分:2)

不存在这样的礼貌。 4.3节中的RX design guideines建议你可以:

  

假设在 OnError OnCompleted 消息后清理资源。

在4.4节中说你可以:

  

尽最大努力停止取消订阅的所有优秀工作

这些指南(“courtesies”)谈论的是运营商发布自己的资源以及尽快获得的资源。

在您的代码中,您没有针对这两种情况进行测试。 Unsubscribe上的ITestableObservable属性的目的是报告观察者取消订阅的时间是否明确处理,而不是在内部清理发生时 - 但是您没有存储这个句柄能够处理它:

xs.Subscribe(ob); /* return of handle ignored here */

所以你试图断言你已经放弃了你丢弃的订阅,而不是你订阅的可观察量清理了它可能已经取消的任何订阅和资源。

如果你想看到4.3 / 4.4的及时资源清理的效果,写一个像这样的扩展方法:

public static IObservable<T> SpyResourceCleanUp<T>(
    this IObservable<T> source, IScheduler scheduler)
{
    return Observable.Create<T>(obs =>
    {
        var subscription = source.Subscribe(obs);
        return new CompositeDisposable(
            subscription,
            Disposable.Create(() => Console.WriteLine(
                "Clean up performed at " + scheduler.Now.Ticks)));
    });
}

并替换你的行:

xs.Subscribe(ob);

xs.SpyResourceCleanUp(ts).Subscribe(ob);

(在某些评论中编辑)

在我的测试中,我看到了我所期望的即时资源清理。通过此更改,您的测试现在将通过,因为SpyResourceCleanUp只要OnCompletes()本身符合4.3的准则,就会取消订阅它的父(xs)。

这里可能不明显的是,Observable.Create只要 订阅处理Dispose()的{​​{1}}方法IDisposableOnComplete()。这就是OnError()帮助您实现第4.3节的原因,以及为什么测试会随着更改的代码传递。

在封面下,Create返回的AnonymousObservable<T> : ObservableBase<T>订阅由AutoDetachObserver as you can see here包裹。

即。您从Create返回的Disposable不是调用者获得的Observable.Create - 他们会获得一个包装版本,可以在流终止或取消时调用您的 Dispose()。< / p>