Reactive Extensions Test Scheduler模拟时间过去

时间:2012-05-24 19:31:07

标签: c# unit-testing system.reactive

我正在使用.Schedule(DateTimeOffset,Action>)内容处理RX调度程序类。基本上我有一个可以安排自己的预定行动。

代码:

public SomeObject(IScheduler sch, Action variableAmountofTime)
{
    this.sch = sch;
    sch.Schedule(GetNextTime(), (Action<DateTimeOffset> runAgain =>
    {
        //Something that takes an unknown variable amount of time.
        variableAmountofTime();

        runAgain(GetNextTime());
    });
}

public DateTimeOffset GetNextTime()
{
    //Return some time offset based on scheduler's 
    //current time which is irregular based on other inputs that i have left out.
    return this.sch.now.AddMinutes(1);
}

我的问题是关于模拟变量AmountofTime可能采用的时间量,并测试我的代码按预期运行,并且只触发按预期调用它。

我已经尝试推进测试调度程序在委托中的时间,但这不起作用。我编写的代码示例不起作用。假设GetNextTime()只是安排一分钟。

[Test]
public void TestCallsAppropriateNumberOfTimes()
{
    var sch = new TestScheduler();

    var timesCalled = 0;

    var variableAmountOfTime = () => 
        { 
            sch.AdvanceBy(TimeSpan.FromMinutes(3).Ticks); 
            timescalled++; 
        };

    var someObject = new SomeObject(sch, variableAmountOfTime);

    sch.AdvanceTo(TimeSpan.FromMinutes(3).Ticks);

    Assert.That(timescalled, Is.EqualTo(1));
}

由于我想要进入未来3分钟,但执行需要3分钟,我希望看到这只触发1次......而不是触发3次。

如何使用测试调度程序模拟在执行期间花费时间的东西。

1 个答案:

答案 0 :(得分:6)

好问题。不幸的是,这在Rx v1.x和Rx v2.0 Beta中是目前不受支持但在上阅读)。让我解释嵌套的Advance *调用给你的复杂性。

基本上,Advance *意味着启动调度程序以运行工作直到指定的点。这涉及在表示虚拟调度程序中的时间流的单个逻辑线程上按顺序运行工作。允许嵌套的Advance *调用会引发一些问题。

首先,嵌套的Advance *调用是否应该导致嵌套的工作循环运行?如果是这种情况,我们将不再模仿单个逻辑执行线程,因为当前工作项将被中断,而有利于运行内部循环。事实上,Advance *会导致隐含的收益,其中在执行所有嵌套工作之前,不允许在Advance *调用之后的其余工作(现在到期)运行。这导致未来的工作不能依赖(或等待)过去的工作来完成其执行的情况。一种方法是引入真正的物理并发,这将打败虚拟时间和历史调度程序的各种设计点。

或者,如果嵌套的Advance *调用以某种方式与最顶层的工作循环调度调用(Advance *或Start)进行通信,则可能需要延长其到期时间,因为嵌套调用已要求前进到超出原始调用的位置截至日期。现在,各种各样的事情变得越来越奇怪了。从Advance *返回后,时钟不会反映更改,并且最高呼叫不再在可预测的时间结束。

对于 Rx v2.0 RC (下个月),我们看了一下这个场景,并认为Advance *不是模仿“时间滑点”的正确方法,因为它需要一个重载意义取决于调用它的上下文。相反,我们引入了一种睡眠方法,可用于从任何上下文中向前推进,而不会产生运行工作的副作用。可以把它想象成一种设置Clock属性的方法,但要防止它回到过去。这个名字也清楚地反映了这一意图。

除了上述内容之外,为了减少嵌套的Advance *调用没有影响的意外因素,我们让它检测到这种情况并在嵌套的上下文中抛出 InvalidOperationException 。另一方面,睡眠可以从任何地方调用。

最后一点说明。事实证明,对于我们在Rx v2.0 RC中所做的工作,我们需要与我们的时间处理完全相同的功能。一些测试需要一种确定性的方法来模拟由于执行可能任意长的用户代码而导致的时间滑动(将OnNext处理程序视为例如Observable.Interval)。

希望这有帮助...请继续关注我们在未来几周内发布的Rx v2.0 RC版本!

-Bart(Rx团队)