測試和偵錯可觀察序列
測試 Rx 應用程式
如果您有一個可觀察的序列,它會在一段長時間內發佈值,則即時測試它可以是延展性。 「反應延伸模組」程式庫提供 TestScheduler 類型,以協助測試這種與時間相關的程式碼,而不需要實際等待時間通過。 TestScheduler 會繼承 VirtualScheduler,並可讓您建立、發佈及訂閱模擬時間中的序列。 例如,您可以壓縮需要 5 天才能完成的出版物,以執行 2 分鐘,同時維持正確的縮放比例。 您也可以擷取過去 (實際發生的序列,例如,過去一年的股票刻度序列) ,並計算或訂閱它,就像是即時推送新的值一樣。
Factory 方法 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 個專案放入緩衝區中,並只在緩衝區已滿時將其傳送為另一個序列。 最後,這會交接給訂閱運算子。 資料會向下傳播所有這些中繼序列,直到推送至觀察者為止。 同樣地,訂用帳戶會以反向方向傳播至來源序列。 藉由在這類傳播中間插入 Do 運算子,您可以在這類資料流程上「spy」,就像在 .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 語句,將相同的值投射為來源。 接著,您可以在語句行設定中斷點 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();
在此範例中,中斷點是在 行中 return y
設定。 當您對程式進行偵錯時, y
變數會顯示在 [ 區域變數 ] 視窗中,而且您可以檢查其總計數 (5)
。 如果您展開 y
,您也可以檢查清單中的每個專案,包括其值和類型。
或者,您可以將 Lambda 運算式轉換成語句 Lambda 運算式、格式化程式碼,讓語句位於自己的行上,然後設定中斷點。
完成偵錯之後,您可以移除任何 Do 和 Select 呼叫。