在使用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
。您还可以围绕即时报价和时间跨度做一些事情来减少那里的噪音。