Sdílet prostřednictvím


Testování a ladění pozorovatelných sekvencí

Testování aplikace Rx

Pokud máte pozorovatelnou sekvenci, která publikuje hodnoty v delším časovém období, testování v reálném čase může být roztažení. Knihovna reaktivního rozšíření poskytuje typ TestScheduler , který pomáhá testovat tento druh kódu závislého na čase, aniž by ve skutečnosti čekal na průchod času. TestScheduler dědí VirtualScheduler a umožňuje vytvářet, publikovat a odebírat sekvence v emulovaném čase. Můžete například komprimovat publikaci, jejíž dokončení trvá 5 dní, do 2minutového běhu při zachování správného měřítka. Můžete také pořídit sekvenci, ke které skutečně došlo v minulosti (např. posloupnost burzovních ticků za předchozí rok) a vypočítat ji nebo předplatit, jako by v reálném čase vysílala nové hodnoty.

Metoda Factory Start provádí všechny naplánované úlohy, dokud fronta není prázdná, nebo můžete určit čas, do, aby se úlohy ve frontě spouštěly pouze v zadaném čase.

Následující příklad vytvoří horkou pozorovatelnou sekvenci se zadanými oznámeními OnNext. Pak spustí plánovač testů a určí, kdy se má přihlásit k odběru a odstranit horkou pozorovatelnou sekvenci. Metoda Start vrátí instanci ITestableObserver, který obsahuje Messages vlastnost, která zaznamenává všechna oznámení v seznamu.

Po dokončení sekvence použijeme metodu ReactiveAssert.AreElementEqual k porovnání vlastnosti Messages spolu se seznamem očekávaných hodnot, abychom zjistili, zda jsou obě stejné (se stejným počtem položek a položky jsou stejné a ve stejném pořadí). Tímto způsobem můžeme potvrdit, že jsme skutečně obdrželi oznámení, která očekáváme. Vzhledem k tomu, že v našem příkladu začneme odebírat pouze 150, přijdeme o hodnotu abc. Když ale porovnáme hodnoty, které jsme zatím obdrželi na hodnotě 400, zjistíme, že po přihlášení k odběru sekvence jsme ve skutečnosti obdrželi všechny publikované hodnoty. A také ověříme, že oznámení OnCompleted bylo aktivováno ve správný čas v 500. Kromě toho jsou informace o předplatném také zachyceny typem ITestableObservable vráceným Metodou CreateHotObservable.

Stejným způsobem můžete pomocí reactiveAssert.AreElementsEqual potvrdit, že k odběrům skutečně došlo v očekávaných časech.

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

Ladění aplikace Rx

K ladění aplikace Rx můžete použít operátor Do. Operátor Do umožňuje určit různé akce, které se mají provést pro každou položku pozorovatelné sekvence (např. tisk nebo protokolování položky atd.). To je užitečné zejména v případě, že zřetězujete mnoho operátorů a chcete vědět, jaké hodnoty se vytvářejí na jednotlivých úrovních.

V následujícím příkladu použijeme příklad vyrovnávací paměti, který generuje celá čísla každou sekundu a zároveň je vloží do vyrovnávací paměti, která může obsahovat 5 položek. V našem původním příkladu v tématu Dotazování pozorovatelných sekvencí pomocí operátorů LINQ odebíráme pouze poslední sekvenci Observable(IList<>), pokud je vyrovnávací paměť plná (a před vyprázdněním). V tomto příkladu však použijeme operátor Do k vytištění hodnot, když jsou vysunuty původní sekvencí (celé číslo každou sekundu). Když je vyrovnávací paměť plná, pomocí operátoru Do vytiskneme stav, než to všechno předáme jako poslední sekvenci, aby se pozorovatel přihlásil k odběru.

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

Jak je vidět z této ukázky, předplatné je na straně příjemce řady zřetězených pozorovatelných sekvencí. Nejprve vytvoříme pozorovatelnou sekvenci celých čísel oddělených sekundou pomocí operátoru Interval. Potom vložíme 5 položek do vyrovnávací paměti pomocí operátoru Buffer a odešleme je jako další sekvenci pouze v případě, že je vyrovnávací paměť plná. Nakonec se předá operátoru Přihlásit k odběru. Data šíří všechny tyto mezisekvence, dokud nebudou nasdílena pozorovateli. Stejným způsobem se předplatná šíří opačným směrem do zdrojové sekvence. Vložením operátoru Do do uprostřed takového šíření můžete tento tok dat "špehovat", stejně jako k ladění používáte Console.WriteLine v .NET nebo printf() v jazyce C.

Operátor časového razítka můžete také použít k ověření času, kdy je položka vysunuta pozorovatelnou sekvencí. To vám může pomoct při řešení potíží s operacemi založenými na čase, abyste zajistili přesnost. Vzpomeňte si na následující příklad z tématu Vytváření a přihlášení k odběru jednoduchých pozorovatelných sekvencí , ve kterém zřetězeme operátor časového razítka s dotazem tak, aby se každá hodnota vysílaná ze zdrojové sekvence připojila k době, kdy je publikována. Když se tak přihlásíme k odběru této zdrojové sekvence, můžeme získat její hodnotu i časové razítko.

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

Výstup bude podobný tomuto:

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

Pomocí operátoru časového razítka jsme ověřili, že první položka je skutečně vysunuta 5 sekund po sekvenci a každá položka se publikuje o 1 sekundu později.

Kromě toho můžete také nastavit zarážky uvnitř výrazů lambda, které vám pomůžou s laděním. Za normálních okolností můžete nastavit zarážku jenom pro celý dotaz, aniž byste museli určit konkrétní hodnotu, abyste ji mohli vyhledat. Pokud chcete toto omezení obejít, můžete doprostřed dotazu vložit operátor Select a nastavit zarážku a v příkazu Select promítnout stejnou hodnotu jako zdroj pomocí příkazu return na vlastním řádku. Pak můžete nastavit zarážku na return řádku příkazu a zkoumat hodnoty, které procházejí dotazem.

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

V tomto příkladu je zarážka nastavená return y na řádku. Při ladění programu se y proměnná zobrazí v okně Místní hodnoty a můžete zkontrolovat její celkový počet (5). Pokud rozbalíte y, můžete také prozkoumat každou položku v seznamu, včetně její hodnoty a typu.

Případně můžete převést výraz lambda na výraz lambda, formátovat kód tak, aby příkaz byl na vlastním řádku, a pak nastavit zarážku.

Po dokončení ladění můžete odebrat všechna volání Do a Select.

Viz také

Koncepty

Vytváření a přihlášení k odběru jednoduchých pozorovatelných sekvencí
Dotazování pozorovatelných sekvencí pomocí operátorů LINQ
Použití plánovačů