Reactive Extensions Test Scheduler模拟时间过去

Mik*_*num 2 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);
}
Run Code Online (Sandbox Code Playgroud)

我的问题是关于模拟变量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));
}
Run Code Online (Sandbox Code Playgroud)

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

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

小智 6

好问题.不幸的是,Rx v1.x和Rx v2.0 Beta 目前不支持此功能(但请继续阅读).让我解释嵌套的Advance*调用给你的复杂性.

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

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

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

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

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

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

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

-Bart(Rx团队)