[英]When using TestScheduler to fire events into Observable.FromEventPattern that has an ObserveOn, the events aren't observed until next event is fired
我似乎遇到了使用帶有ObserveOn的Observable.FromEventPattern的TestScheduler的特定設置的問題。 似乎正在發生的事情是事件都被解雇但我只觀察第二個事件被觸發時的第一個事件。 當然我可以在這里完成一些完全愚蠢的事情,但我無法看清我做錯了什么。 此外,我可能完全錯過了一個觀點或技巧:-)
有人可以向我解釋我在知識中缺少什么,或者我做錯了只看到1個事件,如果我提前2點?
我已經閱讀了關於Lee Campbells的大部分信息http://www.introtorx.com/ (我找到了一個極好的知識源泉:-))
我在用:
Moq V4.2.1409.1722
Rx V 2.2.5.0
XUnit V 1.9.2.1705
這是我的代碼。 只是我自己的事件args所以我可以看到正在觀察的數據。
public class MyEventArgs : EventArgs
{
public int Data { get; private set; }
public MyEventArgs(int data)
{
Data = data;
}
}
帶有將被模擬的EventHandler的接口。
public interface ITmp
{
event EventHandler<MyEventArgs> tmpEvent;
}
具有觀察者並監視來自傳入的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
物品進來...... 2
斷言失敗,因為它此時僅觀察到1個事件。 我已經逐步完成了這一點,並觀察到第二個事件發生后才會發現第一個事件。
作為旁注,如果我添加另一個AdvanceBy(1)或者如果我使用testScheduler.Start而不是AdvanceBy,或者如果我刪除ObserveOn並將調度程序傳遞給FromEventPattern,則此測試將起作用。
此代碼的行為完全符合我的預期。
在測試中,您在測試計划程序上安排兩個操作來引發事件。 一個在時間T = 1,一個在時間T = 2。
然后將測試調度程序推進到時間T = 1。 此時,測試計划程序會檢查其計划的操作以查看需要運行的操作。 它將選擇第一個提升事件的動作。
此事件將被事件的可觀察訂閱者觸發並接收 - 這是由ObserveOn
運算符引起的訂閱。 然后,這將安排一個調用,以便盡快在測試調度程序上向其訂戶提升OnNext
- 測試調度程序將不會執行此調度操作,直到下一次提前 。
這絕對是設計上的。 目的是能夠控制和觀察級聯操作,並模擬Rx事件調度和執行需要一些時間的現實。 你真的不想要任何其他方式。
因此, 在下一個時間片上,T = 2,第一個事件被引發(它首先被調度),然后第一個事件的OnNext
調用被ObserveOn
到ObserveOn
的訂閱者。 等等。
可以這樣想 - 通過調度程序的每個操作至少花費一個單位時間。
要看到這一點,如果刪除ObserveOn
行,則刪除中間調度,並且所寫的測試將通過。
通常在編寫反應測試時,您需要考慮這些影響並調整您的斷言。 出於這個原因,作為一種最佳實踐,我建議將調度操作分開至少1000個滴答。 我傾向於使用像TimeSpan.FromSeconds(x).Ticks
表達式來獲取TimeSpan.FromSeconds(x).Ticks
並且經常有類似TimeSpan.FromSeconds(x).Ticks + epsilon
斷言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
派生測試類,它給出Assert中使用的OnNext
輔助方法。 使用Start
而不是AdvanceBy
運行測試調度程序,直到調度程序隊列為空。 使用測試調度程序創建的觀察者( results
)讓我們可以記錄事件以及它們何時容易發生,並輕松地為此進行斷言。
請注意,您可以使用謂詞來測試記錄的事件來代替事件有效負載以進行更復雜的測試 - 例如,在斷言中使用eventArgs1
,您可以執行類似args => args.Data == 1
。 在這里,我通過在我的訂閱中Select
輸出事件有效負載來簡化檢查,以啟用更可讀的等式檢查。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.