简体   繁体   English

当使用TestScheduler将事件触发到具有ObserveOn的Observable.FromEventPattern时,在下一个事件被触发之前不会观察到事件

[英]When using TestScheduler to fire events into Observable.FromEventPattern that has an ObserveOn, the events aren't observed until next event is fired

I seem to be having a problem with a particular setup of using TestScheduler with The Observable.FromEventPattern that has an ObserveOn. 我似乎遇到了使用带有ObserveOn的Observable.FromEventPattern的TestScheduler的特定设置的问题。 What seems to be happening is that the events are both fired but I only observe the first event when the second event has fired. 似乎正在发生的事情是事件都被解雇但我只观察第二个事件被触发时的第一个事件。 Of course I could have done something completely silly here and I just can't see what I am doing wrong. 当然我可以在这里完成一些完全愚蠢的事情,但我无法看清我做错了什么。 Also I may have completely missed a point or trick :-) 此外,我可能完全错过了一个观点或技巧:-)

Could someone explain to me what I am missing in knowledge or what I am doing wrong to only see 1 event, if I advance by 2? 有人可以向我解释我在知识中缺少什么,或者我做错了只看到1个事件,如果我提前2点?

I have read most of my information on Lee Campbells http://www.introtorx.com/ (which I have found a superb fountain of knowledge :-) ) 我已经阅读了关于Lee Campbells的大部分信息http://www.introtorx.com/ (我找到了一个极好的知识源泉:-))

I am using: 我在用:

Moq V4.2.1409.1722 Moq V4.2.1409.1722

Rx V 2.2.5.0 Rx V 2.2.5.0

XUnit V 1.9.2.1705 XUnit V 1.9.2.1705

This is my code. 这是我的代码。 Just my own event args so I can watch the piece of data that is being observed. 只是我自己的事件args所以我可以看到正在观察的数据。

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

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

An interface with an EventHandler which will be mocked. 带有将被模拟的EventHandler的接口。

public interface ITmp
{
    event EventHandler<MyEventArgs> tmpEvent;
}

The class that has the observer and monitors the events from the tmp object passed in, it also takes a scheduler so I can test this observer. 具有观察者并监视来自传入的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;
    }
}

The test. 考试。

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);
    }
}

Console Output: 控制台输出:

Item came in ...1 物品进来...... 1

Item came in ...2 物品进来...... 2

The assert fails as it has only observed 1 event at this point. 断言失败,因为它此时仅观察到1个事件。 I have stepped through this and have observed that the first event is not observed until the second event has fired. 我已经逐步完成了这一点,并观察到第二个事件发生后才会发现第一个事件。

As a side note this test will work if I add another AdvanceBy(1) or if I use testScheduler.Start instead of the AdvanceBy's, or if I remove the ObserveOn and pass the scheduler into the FromEventPattern. 作为旁注,如果我添加另一个AdvanceBy(1)或者如果我使用testScheduler.Start而不是AdvanceBy,或者如果我删除ObserveOn并将调度程序传递给FromEventPattern,则此测试将起作用。

This code is behaving exactly as I would expect. 此代码的行为完全符合我的预期。

In your test, you schedule two actions on the test scheduler to raise events. 在测试中,您在测试计划程序上安排两个操作来引发事件。 One at time T=1 and one at time T=2. 一个在时间T = 1,一个在时间T = 2。

Then you advance the test scheduler to time T=1. 然后将测试调度程序推进到时间T = 1。 At this point, the test scheduler examines it's scheduled actions to see what needs to be run. 此时,测试计划程序会检查其计划的操作以查看需要运行的操作。 It will select the first action to raise the event. 它将选择第一个提升事件的动作。

This event will be fired and picked up by the observable subscriber to the event - which is the subscription due to the ObserveOn operator. 此事件将被事件的可观察订阅者触发并接收 - 这是由ObserveOn运算符引起的订阅。 This will then schedule a call to raise an OnNext to it's subscriber on the test scheduler as soon as possible - the test scheduler will not execute this scheduled action until time is next advanced . 然后,这将安排一个调用,以便尽快在测试调度程序上向其订户提升OnNext - 测试调度程序将不会执行此调度操作,直到下一次提前

This is absolutely by design. 这绝对是设计上的。 The intention is to be able to control and observe the cascade of operations and simulate the reality that it takes some amount of time for Rx events to be scheduled and executed. 目的是能够控制和观察级联操作,并模拟Rx事件调度和执行需要一些时间的现实。 You really wouldn't want it any other way. 你真的不想要任何其他方式。

So on the next timeslice, T=2, the second event is raised first (it was scheduled first) and then the OnNext call for the first event is fired on the subscriber to the ObserveOn . 因此, 在下一个时间片上,T = 2,第一个事件被引发(它首先被调度),然后第一个事件的OnNext调用被ObserveOnObserveOn的订阅者。 And so on. 等等。

Think of it like this - every action passing through a scheduler costs at least one unit of time. 可以这样想 - 通过调度程序的每个操作至少花费一个单位时间。

To see this, if you remove the ObserveOn line, you remove the intermediate scheduling and the test as written will pass. 要看到这一点,如果删除ObserveOn行,则删除中间调度,并且所写的测试将通过。

Quite often when writing reactive tests, you need to take account of these effects and adjust your assertions. 通常在编写反应测试时,您需要考虑这些影响并调整您的断言。 For this reason, as a best practice I would advise scheduling actions at least 1000 ticks apart. 出于这个原因,作为一种最佳实践,我建议将调度操作分开至少1000个滴答。 I tend to get ticks with expressions like TimeSpan.FromSeconds(x).Ticks and often have asserts like TimeSpan.FromSeconds(x).Ticks + epsilon where epsilon is some expected small constant. 我倾向于使用像TimeSpan.FromSeconds(x).Ticks表达式来获取TimeSpan.FromSeconds(x).Ticks并且经常有类似TimeSpan.FromSeconds(x).Ticks + epsilon断言TimeSpan.FromSeconds(x).Ticks + epsilon其中epsilon是一些预期的小常量。

I have also used helper methods to assert the time is within some expected small range of ticks from a stated point - this can help make tests more readable and avoid having to adjust everything when you make subtle changes. 我还使用了辅助方法断言时间在一个规定点的某些预期的小范围内 - 这可以帮助使测试更具可读性,并避免在进行细微更改时调整所有内容。

So all that said, a more idiomatic way to write your test would be as follows: 所有这一切,编写测试的更惯用的方法如下:

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));
    }
}

Here we derive the test class from ReactiveTest which gives as the OnNext helper method used in the Assert. 这里我们从ReactiveTest派生测试类,它给出Assert中使用的OnNext辅助方法。 Using Start rather than AdvanceBy runs the test scheduler until the scheduler queue is empty. 使用Start而不是AdvanceBy运行测试调度程序,直到调度程序队列为空。 Using the test scheduler created observer ( results ) lets us record events and when they occured easily, and easily assert for that. 使用测试调度程序创建的观察者( results )让我们可以记录事件以及它们何时容易发生,并轻松地为此进行断言。

Note you can use a predicate to test the recorded event in place of the event payload for more complex tests - eg instead of using eventArgs1 in the assertion you could do something like args => args.Data == 1 . 请注意,您可以使用谓词来测试记录的事件来代替事件有效负载以进行更复杂的测试 - 例如,在断言中使用eventArgs1 ,您可以执行类似args => args.Data == 1 Here I simplified the check by Select ing out the event payload in my subscription to enable a more readable equality check. 在这里,我通过在我的订阅中Select输出事件有效负载来简化检查,以启用更可读的等式检查。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Observable.fromEventPattern TypedEventHandler - Observable.fromEventPattern TypedEventHandler ObservableCollection上的Observable.FromEventPattern - Observable.FromEventPattern on ObservableCollection 何时使用Observable.FromEventPattern而不是Observable.FromEvent? - When to use Observable.FromEventPattern rather than Observable.FromEvent? 在 Reactive Extensions for .NET 中使用 Observable.FromEventPattern 时如何避免任何阻塞? - How can I avoid any blocking when using Observable.FromEventPattern in Reactive Extensions for .NET? 如何将使用Rx Observable.FromEventPattern编写的用于事件处理程序的函数转换为纯.net事件处理程序(不带Rx) - How to convert a function written using Rx Observable.FromEventPattern for event handlers to a pure .net event handler(without Rx) 用 Observable.Create 包裹 Observable.FromEventPattern - Wrap Observable.FromEventPattern with Observable.Create 为什么Observable.FromEventPattern接受调度程序? - Why Observable.FromEventPattern take in a scheduler? Observable.FromEventPattern(addHandler,removeHandler)-简化吗? - Observable.FromEventPattern(addHandler, removeHandler ) - simplification? 您如何向Observable.FromEventPattern中使用的事件注册/注销处理程序? - How do you register/unregister handlers to the event used in Observable.FromEventPattern? 使用UpdatePanel时不会触发事件 - Events doesn't fire when using UpdatePanel
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM