Testowanie i debugowanie obserwowanych sekwencji
Testowanie aplikacji Rx
Jeśli masz możliwą do obserwacji sekwencję, która publikuje wartości w dłuższym okresie czasu, testowanie jej w czasie rzeczywistym może być rozciągnięciem. Biblioteka reaktywnego rozszerzenia udostępnia typ TestScheduler , aby ułatwić testowanie tego rodzaju kodu zależnego od czasu bez oczekiwania na upływ czasu. TestScheduler dziedziczy element VirtualScheduler i umożliwia tworzenie, publikowanie i subskrybowanie sekwencji w czasie emulacji. Można na przykład skompaktować publikację, która trwa 5 dni do wykonania w ciągu 2 minut, zachowując poprawną skalę. Możesz również wziąć sekwencję, która rzeczywiście wystąpiła w przeszłości (np. sekwencja akcji kleszczy dla poprzedniego roku) i obliczyć lub zasubskrybować ją tak, jakby wypychała nowe wartości w czasie rzeczywistym.
Metoda factory Start wykonuje wszystkie zaplanowane zadania, dopóki kolejka nie będzie pusta, lub można określić czas, aby zadania w kolejce zostały wykonane tylko do określonego czasu.
W poniższym przykładzie tworzona jest sekwencja z możliwością obserwacji gorącą z określonymi powiadomieniami OnNext. Następnie uruchamia harmonogram testów i określa, kiedy zasubskrybować i usunąć gorącą sekwencję obserwowaną. Metoda Start zwraca wystąpienie ITestableObserver, które zawiera właściwość Messages, która rejestruje wszystkie powiadomienia na liście.
Po zakończeniu sekwencji użyjemy metody ReactiveAssert.AreElementEqual, aby porównać właściwość Messages z listą oczekiwanych wartości, aby sprawdzić, czy obie są identyczne (z tą samą liczbą elementów, a elementy są równe i w tej samej kolejności). Dzięki temu możemy potwierdzić, że rzeczywiście otrzymaliśmy powiadomienia, których oczekujemy. W naszym przykładzie, ponieważ zaczniemy subskrybować tylko wartość 150, wartość zostanie pominięta abc
. Jednak podczas porównywania wartości, które otrzymaliśmy do tej pory na 400, zauważyliśmy, że w rzeczywistości otrzymaliśmy wszystkie opublikowane wartości po zasubskrybowaniu sekwencji. Sprawdzimy również, czy OnCompleted
powiadomienie zostało wyzwolone w odpowiednim czasie o 500. Ponadto informacje o subskrypcji są również przechwytywane przez typ ITestableObservable zwrócony przez metodę CreateHotObservable.
W ten sam sposób można użyć reaktywnej Asssert.AreElementsEqual, aby potwierdzić, że subskrypcje rzeczywiście miały miejsce w oczekiwanym czasie.
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)
});
}
}
Debugowanie aplikacji Rx
Możesz użyć operatora Do debugowania aplikacji Rx. Operator Do umożliwia określenie różnych akcji do wykonania dla każdego elementu obserwowanej sekwencji (np. drukowania lub rejestrowania elementu itp.). Jest to szczególnie przydatne w przypadku łączenia łańcuchów wielu operatorów i chcesz wiedzieć, jakie wartości są generowane na każdym poziomie.
W poniższym przykładzie użyjemy przykładu buforu, który generuje liczby całkowite co sekundę, umieszczając je w buforach, które mogą zawierać 5 elementów. W naszym oryginalnym przykładzie w temacie Wykonywanie zapytań sekwencji obserwowanych przy użyciu operatorów LINQ subskrybujemy tylko ostateczną sekwencję Obserwowalny (IList<>), gdy bufor jest pełny (i przed opróżnieniem). W tym przykładzie użyjemy jednak operatora Do, aby wyświetlić wartości wypychane przez oryginalną sekwencję (liczbę całkowitą co sekundę). Gdy bufor jest pełny, użyjemy operatora Do, aby wydrukować stan, przed przekazaniem wszystkiego jako ostatniej sekwencji dla obserwatora do zasubskrybowania.
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 widać w tym przykładzie, subskrypcja znajduje się na końcu odbiorcy serii łańcuchowych obserwowanych sekwencji. Na początku tworzymy możliwą do obserwowania sekwencję liczb całkowitych oddzieloną sekundą przy użyciu operatora Interwał. Następnie umieszczamy 5 elementów w buforze przy użyciu operatora Bufor i wysyłamy je jako kolejną sekwencję tylko wtedy, gdy bufor jest pełny. Na koniec jest to przekazywane do operatora Subskrybuj. Dane propagują wszystkie te sekwencje pośrednie do momentu wypchnięcia ich do obserwatora. W ten sam sposób subskrypcje są propagowane w odwrotnym kierunku do sekwencji źródłowej. Wstawiając operator Do w środku takich propagacji, możesz "szpiegować" w takim przepływie danych, podobnie jak w przypadku korzystania z elementu Console.WriteLine na platformie .NET lub printf() w języku C w celu przeprowadzenia debugowania.
Możesz również użyć operatora znacznika czasu, aby sprawdzić czas wypchnięcia elementu przez sekwencji, którą można zaobserwować. Może to pomóc w rozwiązywaniu problemów z operacjami opartymi na czasie, aby zapewnić dokładność. Przypomnij sobie poniższy przykład z tematu Tworzenie i subskrybowanie do tematu Simple Observable Sequences , w którym utworzymy łańcuch operator znacznika czasu do zapytania, aby każda wartość wypchnięta przez sekwencję źródłową była dołączana do czasu jego opublikowania. Dzięki temu, gdy subskrybujemy tę sekwencję źródłową, możemy otrzymać zarówno jej wartość, jak i znacznik czasu.
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();
Dane wyjściowe będą podobne do następujących:
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
Korzystając z operatora Znacznik czasu, sprawdziliśmy, że pierwszy element jest rzeczywiście wypychany 5 sekund po sekwencji, a każdy element zostanie opublikowany 1 sekundę później.
Ponadto można również ustawić punkty przerwania wewnątrz wyrażeń lambda, aby ułatwić debugowanie. Zwykle można ustawić tylko punkt przerwania dla całego zapytania bez śpiewania określonej wartości, aby ją wyszukać. Aby obejść to ograniczenie, możesz wstawić operator Select w środku zapytania i ustawić punkt przerwania, a następnie w instrukcji Select projektuj identyczną wartość jako źródło przy użyciu instrukcji return w wierszu. Następnie można ustawić punkt przerwania w return
wierszu instrukcji i zbadać wartości podczas przechodzenia przez zapytanie.
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();
W tym przykładzie punkt przerwania return y
jest ustawiany w wierszu. Podczas debugowania w programie zmienna y
jest wyświetlana w oknie Ustawienia lokalne i można sprawdzić jej łączną liczbę (5)
. Jeśli rozszerzysz węzeł y
, możesz również zbadać każdy element na liście, w tym jego wartość i typ.
Alternatywnie można przekonwertować wyrażenie lambda na wyrażenie lambda instrukcji, sformatować kod, aby instrukcja znajduje się na własnym wierszu, a następnie ustawić punkt przerwania.
Po zakończeniu debugowania możesz usunąć wszystkie wywołania Do i Wybierz.
Zobacz też
Pojęcia
Tworzenie i subskrybowanie prostych sekwencji obserwowanych
Wykonywanie zapytań względem sekwencji obserwowanych przy użyciu operatorów LINQ
Korzystanie z usługi Schedulers