如果你有一個可觀察的序列在一個延長的時間內發佈值,實時測試可以是一個伸展。 Reactive Extension庫提供TestScheduler類型,以幫助測試這種時間依賴代碼,而不需要等待時間通過。 TestScheduler繼承VirtualScheduler並允許您在仿真時間創建,發佈和訂閱序列。例如,您可以壓縮出版物,需要5天才能完成到2分鐘運行,同時保持正確的比例。您還可以採用實際上在過去發生的序列(例如,前一年的股票行情序列),並計算或訂閱該序列,就像實時推出新值一樣。
工廠方法Start執行所有計劃任務,直到隊列爲空,或者您可以指定一個時間,以便排隊的任務只執行到指定的時間。
以下示例使用指定的OnNext通知創建熱可觀察序列。然後它啓動測試調度程序,並指定何時訂閱和處理熱可觀察序列。 Start方法返回ITestableObserver的實例,它包含一個記錄列表中所有通知的Messages屬性。
在序列完成之後,我們使用ReactiveAssert.AreElementEqual方法來比較Messages屬性以及期望值的列表,以查看兩者是否相同(具有相同數量的項目,項目相等且順序相同) 。通過這樣做,我們可以確認我們確實收到了我們期望的通知。在我們的例子中,由於我們只開始在150訂閱,我們將錯過abc值。然而,當我們比較到目前爲止在400處的值時,我們注意到,在我們訂閱序列之後,我們實際上已經收到了所有公佈的值。我們還驗證OnCompleted通知在正確的時間在500處觸發。此外,訂閱信息也由CreateHotObservable方法返回的ITestableObservable類型捕獲。
以同樣的方式,您可以使用ReactiveAssert.AreElementsEqual來確認訂閱確實發生在預期的時間。
using System;
using System.Reactive;
using System.Reactive.Linq;
using Microsoft.Reactive.Testing;
class Program : ReactiveTest
{
static void Main(string[] args)
{
var scheduler = new TestScheduler();
var input = scheduler.CreateHotObservable(
OnNext(100, "abc"),
OnNext(200, "def"),
OnNext(250, "ghi"),
OnNext(300, "pqr"),
OnNext(450, "xyz"),
OnCompleted<string>(500)
);
var results = scheduler.Start(
() => input.Buffer(() => input.Throttle(TimeSpan.FromTicks(100), scheduler))
.Select(b => string.Join(",", b)),
created: 50,
subscribed: 150,
disposed: 600);
ReactiveAssert.AreElementsEqual(results.Messages, new Recorded<Notification<string>>[] {
OnNext(400, "def,ghi,pqr"),
OnNext(500, "xyz"),
OnCompleted<string>(500)
});
ReactiveAssert.AreElementsEqual(input.Subscriptions, new Subscription[] {
Subscribe(150, 500),
Subscribe(150, 400),
Subscribe(400, 500)
});
}
}
調試Rx應用程序
您可以使用Do操作符調試Rx應用程序。 Do操作符允許您指定爲可觀察序列的每個項目採取的各種動作(例如,打印或記錄項目等)。這在您鏈接許多運算符並且想要知道在每個級別生成什麼值時尤其有用。
在下面的例子中,我們將重用Buffer示例,每秒生成一個整數,同時將它們放入可以容納5個項目的緩衝區中。在我們使用LINQ運算符的查詢可觀察序列的原始示例中,當緩衝區已滿(並且在其被清空之前)時,我們僅訂閱最終的Observable(IList <>)序列。然而,在這個例子中,我們將使用Do操作符,當它們被原始序列(每秒一個整數)推出時,打印出值。當緩衝區已滿時,我們使用Do操作符打印狀態,然後將所有這些作爲觀察者訂閱的最後序列。
var seq1 = Observable.Interval(TimeSpan.FromSeconds(1))
.Do(x => Console.WriteLine(x.ToString()))
.Buffer(5)
.Do(x => Console.WriteLine("buffer is full"))
.Subscribe(x => Console.WriteLine("Sum of the buffer is " + x.Sum()));
Console.ReadKey();
從此示例中可以看出,訂閱位於一系列鏈接的可觀察序列的收件人端。首先,我們使用Interval運算符創建一個可觀察的整數序列,以秒爲單位。然後,我們使用Buffer操作符將5個項目放入一個緩衝區中,並且只有當緩衝區已滿時纔將它們作爲另一個序列發送出去。最後,這被交給Subscribe運算符。數據沿着所有這些中間序列傳播,直到它們被推送到觀察者。以相同的方式,訂閱以與源序列相反的方向傳播。通過在這樣的傳播過程中插入Do操作符,您可以對這種數據流進行“間諜”,就像在.NET中使用Console.WriteLine或在C中使用printf()執行調試一樣。
您還可以使用Timestamp運算符來驗證項目按可觀察序列推出的時間。這可以幫助您排除基於時間的操作,以確保準確性。回想一下創建和訂閱簡單可觀察序列主題中的以下示例,其中我們將Timestamp運算符鏈接到查詢,以便源序列推出的每個值將在發佈時附加。通過這樣做,當我們訂閱這個源序列時,我們可以接收它的值和時間戳。
Console.WriteLine(“Current Time: “ + DateTime.Now);
var source = Observable.Timer(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(1))
.Timestamp();
using (source.Subscribe(x => Console.WriteLine("{0}: {1}", x.Value, x.Timestamp)))
{
Console.WriteLine("Press any key to unsubscribe");
Console.ReadKey();
}
Console.WriteLine("Press any key to exit");
Console.ReadKey();
輸出如下:
Current Time: 5/31/2011 5:35:08 PM
Press any key to unsubscribe
0: 5/31/2011 5:35:13 PM -07:00
1: 5/31/2011 5:35:14 PM -07:00
2: 5/31/2011 5:35:15 PM -07:00
通過使用Timestamp運算符,我們驗證了第一個項目確實在序列之後5秒被推出,每個項目在1秒後發佈。
此外,您還可以在lambda表達式中設置斷點以協助調試。 通常,您只能爲整個查詢設置斷點,而不必選擇特定值來查看它。 要解決此限制,您可以在查詢中間插入Select運算符,並在其中設置斷點,並在Select語句中,使用自己的行上的return語句將相同的值投影到其源。 然後,您可以在返回語句行設置斷點,並在查找值時檢查值
var seq = Observable.Interval(TimeSpan.FromSeconds(1))
.Do(x => Console.WriteLine(x.ToString()))
.Buffer(5)
.Select(y => {
return y; }) // set a breakpoint at this line
.Do(x => Console.WriteLine("buffer is full"))
.Subscribe(x => Console.WriteLine("Sum of the buffer is " + x.Sum()));
Console.ReadKey();
在此示例中,斷點設置在返回y線。 當調試到程序中時,y變量顯示在Locals窗口中,您可以檢查其總數(5)。 如果您展開y,您還可以檢查列表中的每個項目,包括其值和類型。
或者,您可以將lambda表達式轉換爲語句lambda表達式,格式代碼,以便語句在其自己的行上,然後設置斷點。
完成調試後,可以刪除任何Do和Select調用。