2014-10-16 25 views
1

我似乎遇到了使用TestScheduler和Observable.FromEventPattern的特定设置的问题观察。似乎正在发生的事情是,这些事件都被解雇了,但我只观察了第二个事件发生后的第一个事件。当然,我可以在这里完成一些愚蠢的事情,而我只是看不到我做错了什么。此外,我可能已经完全错过一个点或欺骗:-)当使用TestScheduler将事件触发到具有ObserveOn的Observable.FromEventPattern中时,直到下一个事件被触发时,事件才被观察到

有人能向我解释什么,我缺少知识或我在做什么错,只看到1个事件,如果我通过2推进?

我已经阅读了我的大部分对李坎贝尔http://www.introtorx.com/信息(这是我发现的知识:-)的一个极好的喷泉)

我使用:

起订量V4.2.1409.1722

的Rx V 2.2.5.0

的xUnit V 1.9.2.1705

这是我的代码。 只是我自己的事件参数,所以我可以看到正在观察的数据。

public class MyEventArgs : EventArgs 
{ 
    public int Data { get; private set; } 

    public MyEventArgs(int data) 
    { 
     Data = data; 
    } 
} 

与将被模拟的EventHandler的接口。

具有观察者,并且监控来自传入的TMP对象的事件的类,它也需要一个调度器,所以我可以测试该观察者。

internal class SomeClass2 
{ 
    private IObservable<EventPattern<MyEventArgs>> _observable; 

    public SomeClass2(ITmp tmp, IScheduler scheduler) 
    { 
     _observable = Observable.FromEventPattern<MyEventArgs>(h => tmp.tmpEvent += h, h => tmp.tmpEvent -= h) 
           .Do(next => Console.WriteLine("Item came in...{0}", next.EventArgs.Data)) 
           .ObserveOn(scheduler); 
    } 

    public IObservable<EventPattern<MyEventArgs>> Raw() 
    { 
     return _observable; 
    } 
} 

该测试。

public class Tests 
{ 
    [Fact] 
    public void FactMethodName2() 
    { 
     var mockedTmp = new Mock<ITmp>(); 
     var testScheduler = new TestScheduler(); 
     var temp = new SomeClass2(mockedTmp.Object, testScheduler); 
     var count = 0; 
     var myEventArgsObserved = new List<MyEventArgs>(); 

     temp.Raw().Subscribe(
      next => 
      { 
       count++; 
       myEventArgsObserved.Add(next.EventArgs); 
      }); 

     testScheduler.Schedule(TimeSpan.FromTicks(1),() => mockedTmp.Raise(tmp => tmp.tmpEvent += null, new MyEventArgs(1))); 
     testScheduler.Schedule(TimeSpan.FromTicks(2),() => mockedTmp.Raise(tmp => tmp.tmpEvent += null, new MyEventArgs(2))); 

     testScheduler.AdvanceBy(1); 
     testScheduler.AdvanceBy(1); 

     Assert.Equal(2, count); 
    } 
} 

控制台输出:

项目进来了...... 1

项目进来了...因为它只有在这一点上观察到1个事件2

断言失败。我已经完成了这一步,并观察到第一个事件直到第二个事件发射时才被观察到。

作为一个方面说明,如果我添加另一个AdvanceBy(1)或者如果我使用testScheduler.Start而不是AdvanceBy's,或者如果删除ObserveOn并将调度程序传递到FromEventPattern中,此测试将工作。

回答

2

此代码行为完全按照我期望的那样。

在您的测试,你安排在测试调度两个动作引发事件。一个在时间T = 1和一个在时间T = 2。

然后就前进测试调度到时间T = 1。此时,测试调度程序检查它的计划操作以查看需要运行的操作。它将选择第一个动作来提高事件。

该事件将被可观察的用户触发并接收到该事件 - 这是由于ObserveOn运营商而导致的订阅。这一操作将时间表调用引发OnNext用户在测试调度尽快 - 直到时间是未来先进测试调度器将无法执行此计划的行动。

这绝对是设计。其目的是能够控制和观察级联的操作,并模拟Rx事件需要一定的时间安排和执行的现实情况。你真的不希望任何其他方式。

于是就下一个时间片,T = 2时,该第二事件首次提出(它被调度第一),然后将OnNext呼叫的第一事件是在用户到ObserveOn烧成。等等。

想想这样 - 每个通过调度器的动作至少花费一个单位时间。

要看到这一点,如果您删除ObserveOn行,您将删除中间调度,并按照写入的测试通过。

写作反应测试时经常需要考虑这些影响并调整您的断言。出于这个原因,作为一个最佳实践,我会建议安排行动至少1000分钟。我倾向于使用像TimeSpan.FromSeconds(x).Ticks这样的表达式,并且经常具有像TimeSpan.FromSeconds(x).Ticks + epsilon这样的表达式,其中epsilon是一些预期的小常数。

我也使用了辅助方法来声明时间在某个预期的小范围内,这可以帮助使测试更具可读性并避免在进行细微更改时调整所有内容。

所以所有的说,写你的测试将是如下一个更地道的方式:

public class Tests : ReactiveTest 
{ 
    [Fact] 
    public void FactMethodName2() 
    { 
     var mockedTmp = new Mock<ITmp>(); 
     var testScheduler = new TestScheduler(); 
     var temp = new SomeClass2(mockedTmp.Object, testScheduler); 
     const int c = 1; 

     var eventArgs1 = new MyEventArgs(1); 
     var eventArgs2 = new MyEventArgs(2); 

     var results = testScheduler.CreateObserver<MyEventArgs>(); 

     temp.Raw().Select(ep => ep.EventArgs).Subscribe(results); 

     testScheduler.Schedule(TimeSpan.FromTicks(1000), 
      () => mockedTmp.Raise(tmp => tmp.tmpEvent += null, eventArgs1)); 

     testScheduler.Schedule(TimeSpan.FromTicks(2000), 
      () => mockedTmp.Raise(tmp => tmp.tmpEvent += null, eventArgs2)); 

     testScheduler.Start(); 

     results.Messages.AssertEqual(
      OnNext(1000 + c, eventArgs1), 
      OnNext(2000 + c, eventArgs2)); 
    } 
} 

在这里,我们得到来自ReactiveTest测试类这给作为断言使用的OnNext的辅助方法。使用Start而不是AdvanceBy运行测试调度程序,直到调度程序队列为空。使用测试调度程序创建的观察者(results)可以让我们记录事件以及它们何时轻松发生,并轻松声明。

请注意,您可以使用谓词来测试记录的事件,以取代更复杂测试的事件有效负载 - 例如,而不是在断言中使用eventArgs1,您可以执行类似args => args.Data == 1的操作。在这里,我通过Select简化了我的订阅中的事件有效负载的检查,以启用更具可读性的相等性检查。

+0

如果我想找到你回答我的信息,我是否需要深入研究rx代码?你是如何学习这些信息的? :-)由于我似乎没有找到足够的资源在线,除了introtorx。我对rx也有点新鲜感。我知道它说不写评论说感谢,但我觉得答案和投票是不够的。如果人们不喜欢它,并且我愿意,我可以稍后删除此评论。但是非常感谢你,这是一个非常好的答案,非常清楚,很好的解释,并且给我展示了一种更加美观的测试方式,给了我更多的理解! – RichardWilliams 2014-10-17 18:34:46

+1

很高兴它有帮助。至于我在哪里以及如何来到这里,这里真的没有简单的答案 - 很多地方。 Rx团队博客是一个很好的来源 - 尤其是在测试方面。请参阅[本文](http://blogs.msdn.com/b/rxteam/archive/2012/06/14/testing-rx-queries-using-virtual-time-scheduling.aspx)。我还读了Rx的介绍(干杯李!),我研究了源代码,观看了第9频道的许多视频。我也很幸运能够广泛使用Rx开展商业项目。我还回答了许多关于SO的Rx问题。所以真的是来自许多来源的经验。 – 2014-10-17 22:56:32

相关问题