【问题标题】:Rx .Net TestScheduler- executing immediately scheduled eventsRx .Net TestScheduler - 执行立即安排的事件
【发布时间】:2022-01-18 03:12:30
【问题描述】:

当我这样做时:

testScheduler.Schedule("Hello world",(scheduler, state) => Console.WriteLine(state));
testScheduler.AdvanceTo(testScheduler.Now);

我在VirtualTimeSchedulerBase打了这个代码:

public void AdvanceTo(TAbsolute time)
{
  int num = this.Comparer.Compare(time, this.Clock);
  if (num < 0)
    throw new ArgumentOutOfRangeException("time");
  if (num == 0)
    return;

num == 0 为真,我退出该方法。

我可以致电testScheduler.Start(),我的操作将执行。但随后TestScheduler 将继续执行其队列中的所有内容。而我希望它在当前时间停止执行操作。

我在 TestScheduler 上看不到任何其他可以得到我想要的行为的方法。

这是一个错误,还是正确的行为但我遗漏了什么?

编辑:

我误会了。 TestScheduler 直到在计划日期之后才执行操作。

调度一个动作会立即将其调度为testScheduler.Now 的当前值。所以直到Now + 1才会执行。

  var testScheduler = new TestScheduler();
  var due = new DateTime();
    testScheduler.Schedule("Hello world", due, (scheduler, s) =>
  {
    Console.WriteLine(s);
    return Disposable.Empty;
  });
  testScheduler.AdvanceTo(due.Ticks);
  // Nothing has happened

  testScheduler.AdvanceTo(due.Ticks+1);
  // -> "Hello world"

这仍然不是我想要的行为,但你去吧。

【问题讨论】:

  • "调度一个动作会立即将它调度为 testScheduler.Now 的当前值。所以它不会在 Now + 1 之前执行。" - 不,这是错误的 - 它为 Clock + 1 安排。它仅在您前进时执行,因为那是您给 TestScheduler 一个时间片来处理它的队列的时候。

标签: c# .net system.reactive


【解决方案1】:

您可能需要考虑如何使用TestScheduler:

它将通常在适当的时间执行。例如,此代码将写入控制台:

var scheduler = new TestScheduler();

scheduler.Schedule(
    TimeSpan.FromTicks(100),
    () => Console.WriteLine("Hello"));

scheduler.AdvanceTo(100);

但是,TestScheduler 只会在时间移动时检查它的队列。因此,如果您安排一个操作,您需要通过AdvanceBy、AdvanceTo 或Start 移动时间以使其处理队列。当它这样做时,它将处理直到当前时间的所有内容。例如。尽管安排“过去”,即使这也会输出到控制台:

var scheduler = new TestScheduler();

scheduler.AdvanceTo(TimeSpan.FromTicks(100).Ticks);

scheduler.Schedule(
    DateTime.MinValue + TimeSpan.FromTicks(50),
    () => Console.WriteLine("Hello"));

Console.WriteLine("No output until the scheduler is given time...");
scheduler.AdvanceBy(1);

TestScheduler 的惯用用法通常涉及将所有工作排队,然后通过 Start() 调用运行到完成,然后检查预期状态。使用AdvanceBy、AdvanceTo 往往用于要求更高的测试场景,在这些场景中您需要测试一些中间状态 - 即使这样,您通常也会先将所有内容在适当的到期时间排队,然后再进行@987654331 @ 或 AdvanceTo 您感兴趣的时间,检查状态,然后使用 AdvanceBy、AdvanceTo 或 Start 再次进行。

你不想做的一般是排队工作,运行一点,排队更多的工作,运行一点 - 特别是如果你在没有到期时间的情况下进行调度。然后你会遇到这里的问题。

这并不是说在您的特定情况下这可能是不可避免的 - 例如,如果您需要决定在特定时间之后安排什么 - 但请考虑您是否可以提前设置所有内容,因为这可能会导致更简洁的测试代码,更接近于 Arrange Act Assert 模式。

我尝试通过调度来安排,然后通过移动时间来行动,然后断言结果。

【讨论】:

  • 谢谢詹姆斯。这再次使事情变得更加清晰。我在一个流程测试中,我说 Given/When/Then/When/Then(即我确实关心中间状态)。即便如此, Start() 大部分都可以正常工作。当我安排了一项任务时,我遇到了问题,而该任务又安排了未来的另一项任务。然后调度程序运行到未来。我还将调度程序包装在我的 IClock 接口的实现中,以用作系统时间。我现在已经将我的时钟与调度程序分离,并且我的测试独立地移动了系统时间和 TestScheduler。更冗长但实际上更正确。
【解决方案2】:

使用 AdvanceBy(1) 将调度程序提前 1 个刻度。调度程序仅在时钟实际提前时执行事件。

当您安排立即执行的某事时,不同的调度程序会表现出不同的行为。他们中的一些人确实会立即执行它。有些人将它放在队列中,以便在第一个可用的机会时执行。

TestScheduler 在这种情况下没有很好的行为方式,除非修改 API 以让您告诉它应该采取何种行为方式。

【讨论】:

  • 我不想这样做的原因很长。我想我只需要接受 TestScheduler 的语义。我希望 Rx 的人有充分的理由让它看起来如此。感谢您为我指明正确的方向。
  • TestScheduler 实际上专门对没有指定到期时间的作业进行排队,或者在到期时间为Clock + 1 的情况下延迟时间。这就是 AdvanceBy(1) 起作用的原因......有关更多信息,请参阅我的答案。
【解决方案3】:

Start 方法将执行所有计划。您可以安排 Stop 方法调用以在给定点暂停执行。

        var testScheduler = new TestScheduler();
        var due = new DateTime();
        testScheduler.Schedule("Hello world", due, (scheduler, s) =>
        {
            Console.WriteLine(s);
            return Disposable.Empty;
        });

        testScheduler.Schedule(due.Ticks + 1, (scheduler, s) => testScheduler.Stop());

        testScheduler.Schedule("Do more stuff", due.AddMinutes(1), (scheduler, s) => Console.WriteLine(s));

        testScheduler.Start();
        Assert.IsFalse(testScheduler.IsEnabled);
        Assert.AreEqual(due.Ticks + 1, testScheduler.Clock);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多