Как я могу использовать TestScheduler с временным окном ReplaySubject

У меня есть пример тестового кода:

[Fact]
 public void Should_only_contain_most_recent() {
 var window = TimeSpan.FromMilliseconds(200);
 var results = new ReplaySubject<long>(window);
 results.OnNext(1);
 results.OnNext(2);
 System.Threading.Thread.Sleep(50);
 results.OnNext(3);
 System.Threading.Thread.Sleep(50);
 results.OnNext(4);
 System.Threading.Thread.Sleep(200);
 results.OnNext(5);
 results.OnCompleted();
 var items = results.************();
 Assert.True(items.SequenceEqual(new long [] { 5 }));
 }
</long>

который я хотел бы включить в использование TestScheduler из пакета Microsoft.Reactive.Testing, потому что я хотел бы избавиться от сна здесь.

Я уже пытался сделать что-то подобное.

[Fact]
 public void Should_only_contain_most_recent() {
 var scheduler = new TestScheduler();
 var window = TimeSpan.FromMilliseconds(200);
 var results = new ReplaySubject<long>(window /* Never finishes when add the scheduler here.*/);
 results.OnNext(1);
 results.OnNext(2);
 scheduler.Schedule(TimeSpan.FromMilliseconds(50), () => results.OnNext(3));
 scheduler.Schedule(TimeSpan.FromMilliseconds(50), () => results.OnNext(4));
 scheduler.Schedule(TimeSpan.FromMilliseconds(200), () => results.OnNext(5));
 scheduler.Schedule(TimeSpan.FromMilliseconds(210), results.OnCompleted);
 scheduler.Start();

 var items = results.************();
 Assert.True(items.SequenceEqual(new long[] { 5 }));
 }
</long>

Но я чего-то не хватает.

1 ответ

Предположительно, вы пытаетесь проверить поведение воспроизведения ReplaySubject. Проблема в том, что вам нужно дважды запустить TestScheduler. Он запускается только до тех пор, пока не будут выполнены все запланированные события, но нам нужно загрузить ReplaySubject без какого-либо абонента, чтобы он воспроизводил события, которые он будет воспроизводить, а затем снова запустить планировщик после присоединения подписчика.

Вот идиоматический способ написать этот тест:

Сначала я установил source с необходимыми событиями. Получив ваш тестовый класс из ReactiveTest вы можете воспользоваться вспомогательными методами OnXXX, чтобы легче создавать исходные события.

Как только мы получим исходный поток, мы можем подписаться на ReplaySubject и вызвать " Start в TestScheduler. Это приведет к виртуальному времени до последнего запланированного события (T = 300 мс), которое я продемонстрирую, сбросив тайм-аут.

Теперь мы используем метод TestScheduler CreateObserver для создания рекордера для результатов и подписываем его на ReplaySubject.

Теперь мы снова запускаем TestScheduler - это запускает часы с T = TestScheduler до тех пор, пока новые чеканированные запланированные события не будут сброшены. Это события, ReplaySubject должен воспроизводиться ReplaySubject - те, которые находятся в окне ReplaySubject мс. Обратите внимание, что они будут испускаться при T = 300 мс + 1 тик, + 2 тика и + 3 тика - это потому, что TestScheduler имеет разрешение 1 тик.

Здесь код:

public class MyTests : ReactiveTest
{
 [Fact]
 public void Should_only_contain_most_recent()
 {
 var scheduler = new TestScheduler();

 var source = scheduler.CreateHotObservable(
 OnNext(0, 1),
 OnNext(0, 2),
 OnNext(TimeSpan.FromMilliseconds(50).Ticks, 3),
 OnNext(TimeSpan.FromMilliseconds(100).Ticks, 4),
 OnNext(TimeSpan.FromMilliseconds(300).Ticks, 5),
 OnCompleted(TimeSpan.FromMilliseconds(300).Ticks, witness: 0));

 var replaySubject = new ReplaySubject<int>(
 TimeSpan.FromMilliseconds(200), scheduler);

 source.Subscribe(replaySubject);

 scheduler.Start();

 /* the test scheduler is now at 300 milliseconds
 * and the ReplaySubject is loaded */
 Console.WriteLine(scheduler.Now.Ticks);

 var results = scheduler.CreateObserver<int>();
 replaySubject.Subscribe(results);

 /* run the scheduler on to flush the events from the ReplaySubject */
 scheduler.Start();

 results.Messages.AssertEqual(
 OnNext(TimeSpan.FromMilliseconds(300).Ticks + 1, 4),
 OnNext(TimeSpan.FromMilliseconds(300).Ticks + 2, 5),
 OnCompleted(TimeSpan.FromMilliseconds(300).Ticks + 3, witness: 0));
 }
}
</int></int>

licensed under cc by-sa 3.0 with attribution.