使用TestScheduler订阅失败,直到第二个时钟点



在使用TestScheduler调用SubscribeOn时,我看到以下不一致的行为。

        var testScheduler = new TestScheduler();
        var subject = new Subject<int>();
        testScheduler.Schedule(() => subject.OnNext(1));
        testScheduler.Schedule(TimeSpan.FromTicks(1), () => subject.OnNext(2));
        testScheduler.Schedule(TimeSpan.FromTicks(2), () => subject.OnNext(3));
        testScheduler.Schedule(TimeSpan.FromTicks(3), () => subject.OnNext(4));
        subject
            .SubscribeOn(testScheduler)
            .Subscribe(Console.WriteLine);
        testScheduler.Start();
        Console.ReadKey();

生成输出:

3
4

但是,如果您在计划之前订阅观察器,则它会按预期工作。

        var testScheduler = new TestScheduler();
        var subject = new Subject<int>();
        subject
            .SubscribeOn(testScheduler)
            .Subscribe(Console.WriteLine);
        testScheduler.Schedule(() => subject.OnNext(1));
        testScheduler.Schedule(TimeSpan.FromTicks(1), () => subject.OnNext(2));
        testScheduler.Schedule(TimeSpan.FromTicks(2), () => subject.OnNext(3));
        testScheduler.Schedule(TimeSpan.FromTicks(3), () => subject.OnNext(4));

        testScheduler.Start();
        Console.ReadKey();

产生输出:

1
2
3
4

任何人都可以解释这种行为,或者这是一个错误?

我相信正在发生的事情是,你有效地安排了两件事在同一时间点发生。初始OnNext(1)调用和订阅,即 SubscribeOn计划在时钟周期 0 隐式发生。

当计划同时发生两件事时,首先安排的事情将首先运行,然后运行第二件事,依此类推。它们都将在虚拟时钟上看到相同的时间,但它是单线程的,因此一次只能运行一件事。

在您的第一个示例中,您实际上有一个操作日志,如下所示

Time (in Ticks) Action
---------------------------
       0        () => subject.OnNext(1)
       0        () => subject.Subscribe(..)
10000000        () => subject.OnNext(2)
20000000        () => subject.OnNext(3)
30000000        () => subject.OnNext(4)

在你的第二个例子中,日记看起来更像这样

Time (in Ticks) Action
---------------------------
       0        () => subject.Subscribe(..)
       0        () => subject.OnNext(1)
10000000        () => subject.OnNext(2)
20000000        () => subject.OnNext(3)
30000000        () => subject.OnNext(4)

因此,虽然订阅和OnNext的时间相同,但顺序不同。将testScheduler.Start();视为循环执行操作并推进时钟可能会有所帮助。考虑到这一点,应该很清楚为什么您在第一个示例中看不到值 1。

我有两个建议:

  • 使用TestScheduler创建可观察序列而不是主体。
  • 避免在测试中将事情安排在时间 0。这是现实世界中不太可能发生的事情,它使得看起来很奇怪的测试(你测试事情是否偏离了一个(

你可以像这样重写你的测试

var testScheduler = new TestScheduler();
var observer = testScheduler.CreateObserver<int>();
var sequence = testScheduler.CreateHotObservable(
    ReactiveTest.OnNext(TimeSpan.FromSeconds(1).Ticks, 1),
    ReactiveTest.OnNext(TimeSpan.FromSeconds(2).Ticks, 2),
    ReactiveTest.OnNext(TimeSpan.FromSeconds(3).Ticks, 3),
    ReactiveTest.OnNext(TimeSpan.FromSeconds(4).Ticks, 4)
    );
sequence
    .SubscribeOn(testScheduler)
    .Subscribe(observer);
testScheduler.Start();
var expected = new[]
{
    ReactiveTest.OnNext(TimeSpan.FromSeconds(1).Ticks, 1),
    ReactiveTest.OnNext(TimeSpan.FromSeconds(2).Ticks, 2),
    ReactiveTest.OnNext(TimeSpan.FromSeconds(3).Ticks, 3),
    ReactiveTest.OnNext(TimeSpan.FromSeconds(4).Ticks, 4),
};
CollectionAssert.AreEqual(expected, observer.Messages);

如果你想减少代码中的噪音,你可以对ReactiveTest类进行子类化(在 Rx 测试中(,这样你就可以直接访问OnNext工厂方法及其同级OnError + OnCompleted 。您还可以围绕即时报价和时间跨度做一些事情来减少那里的噪音。

相关内容

  • 没有找到相关文章

最新更新