Partager via


Test et débogage des séquences observables

Test de votre application Rx

Si vous disposez d’une séquence observable qui publie des valeurs sur une période de temps prolongée, le test en temps réel peut être très long. La bibliothèque d’extensions réactives fournit le type TestScheduler pour aider à tester ce type de code dépendant du temps sans attendre que le temps passe. TestScheduler hérite de VirtualScheduler et vous permet de créer, publier et s’abonner à des séquences dans le temps émulé. Par exemple, vous pouvez compacter une publication qui prend 5 jours pour une exécution de 2 minutes, tout en conservant la mise à l’échelle correcte. Vous pouvez également prendre une séquence qui s’est réellement produite dans le passé (par exemple, une séquence de graduations d’actions pour une année précédente) et la calculer ou s’y abonner comme si elle exégait de nouvelles valeurs en temps réel.

La méthode de fabrique Start exécute toutes les tâches planifiées jusqu’à ce que la file d’attente soit vide, ou vous pouvez spécifier une heure pour que les tâches mises en file d’attente soient exécutées uniquement à l’heure spécifiée.

L’exemple suivant crée une séquence observable à chaud avec des notifications OnNext spécifiées. Il démarre ensuite le planificateur de test et spécifie quand s’abonner et supprimer la séquence observable à chaud. La méthode Start retourne une instance du ITestableObserver, qui contient une propriété Messages qui enregistre toutes les notifications dans une liste.

Une fois la séquence terminée, nous utilisons la méthode ReactiveAssert.AreElementEqual pour comparer la propriété Messages , ainsi qu’une liste de valeurs attendues pour voir si les deux sont identiques (avec le même nombre d’éléments, et les éléments sont égaux et dans le même ordre). Ce faisant, nous pouvons confirmer que nous avons bien reçu les notifications que nous attendons. Dans notre exemple, étant donné que nous ne commençons à s’abonner qu’à 150, nous allons manquer la valeur abc. Toutefois, lorsque nous comparons les valeurs que nous avons reçues jusqu’à présent à 400, nous remarquons que nous avons en fait reçu toutes les valeurs publiées après que nous nous sommes abonnés à la séquence. Et nous vérifions également que la OnCompleted notification a été déclenchée au bon moment à 500. En outre, les informations d’abonnement sont également capturées par le type ITestableObservable retourné par la méthode CreateHotObservable.

De même, vous pouvez utiliser ReactiveAssert.AreElementsEqual pour confirmer que les abonnements se sont effectivement produits aux heures prévues.

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

Débogage de votre application Rx

Vous pouvez utiliser l’opérateur Do pour déboguer votre application Rx. L’opérateur Do vous permet de spécifier différentes actions à effectuer pour chaque élément de séquence observable (par exemple, imprimer ou journaliser l’élément, etc.). Cela est particulièrement utile lorsque vous chaînez de nombreux opérateurs et que vous souhaitez savoir quelles valeurs sont produites à chaque niveau.

Dans l’exemple suivant, nous allons réutiliser l’exemple De mémoire tampon qui génère des entiers toutes les secondes, tout en les plaçant dans des mémoires tampons pouvant contenir 5 éléments chacune. Dans notre exemple d’origine dans la rubrique Interrogation des séquences observables à l’aide d’opérateurs LINQ , nous nous abonneons uniquement à la séquence Observable(IList<>) finale lorsque la mémoire tampon est pleine (et avant qu’elle ne soit vidée). Dans cet exemple, toutefois, nous allons utiliser l’opérateur Do pour imprimer les valeurs lorsqu’elles sont poussées par la séquence d’origine (un entier toutes les secondes). Lorsque la mémoire tampon est pleine, nous utilisons l’opérateur Do pour imprimer le status, avant de remettre tout cela en tant que séquence finale à laquelle l’observateur doit s’abonner.

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

Comme vous pouvez le voir dans cet exemple, un abonnement se trouve à l’extrémité du destinataire d’une série de séquences observables chaînées. Au début, nous créons une séquence observable d’entiers séparés par une seconde à l’aide de l’opérateur Interval. Ensuite, nous plaçons 5 éléments dans une mémoire tampon à l’aide de l’opérateur Buffer et les envoyons en tant qu’autre séquence uniquement lorsque la mémoire tampon est pleine. Enfin, il est remis à l’opérateur Subscribe. Les données propagent toutes ces séquences intermédiaires jusqu’à ce qu’elles soient envoyées à l’observateur. De la même façon, les abonnements sont propagés dans le sens inverse vers la séquence source. En insérant l’opérateur Do au milieu de telles propagations, vous pouvez « espionner » ce flux de données, tout comme vous utilisez Console.WriteLine dans .NET ou printf() en C pour effectuer le débogage.

Vous pouvez également utiliser l’opérateur Timestamp pour vérifier l’heure à laquelle un élément est poussé par une séquence observable. Cela peut vous aider à résoudre les problèmes liés aux opérations basées sur le temps pour garantir l’exactitude. Rappelez-vous l’exemple suivant de la rubrique Création et abonnement à des séquences observables simples , dans lequel nous chaînons l’opérateur Timestamp à la requête afin que chaque valeur envoyée par la séquence source soit ajoutée au moment de sa publication. Ce faisant, lorsque nous nous abonneons à cette séquence source, nous pouvons recevoir sa valeur et son horodatage.

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

La sortie doit ressembler à ceci :

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

En utilisant l’opérateur Timestamp, nous avons vérifié que le premier élément est effectivement poussé 5 secondes après la séquence et que chaque élément est publié 1 seconde plus tard.

En outre, vous pouvez également définir des points d’arrêt à l’intérieur d’expressions lambda pour faciliter le débogage. Normalement, vous pouvez uniquement définir un point d’arrêt pour l’ensemble de la requête sans définir une valeur particulière pour la regarder. Pour contourner cette limitation, vous pouvez insérer l’opérateur Select au milieu de la requête et définir un point d’arrêt, puis, dans l’instruction Select, projeter la valeur identique en tant que source à l’aide d’une instruction de retour sur sa propre ligne. Vous pouvez ensuite définir un point d’arrêt sur la ligne d’instruction return et examiner les valeurs au fur et à mesure qu’elles parcourent la requête.

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

Dans cet exemple, le point d’arrêt est défini sur la return y ligne. Lorsque vous déboguez dans le programme, la y variable s’affiche dans la fenêtre Locals et vous pouvez examiner son nombre (5)total . Si vous développez y, vous pouvez également examiner chaque élément de la liste, y compris sa valeur et son type.

Vous pouvez également convertir une expression lambda en expression lambda d’instruction, mettre en forme le code afin qu’une instruction se trouve sur sa propre ligne, puis définir un point d’arrêt.

Vous pouvez supprimer tous les appels Do et Select une fois le débogage terminé.

Voir aussi

Concepts

Création et abonnement à des séquences observables simples
Interrogation de séquences observables à l’aide d’opérateurs LINQ
Utilisation des planificateurs