Compartir a través de


Pruebas y depuración de secuencias observables

Prueba de la aplicación Rx

Si tiene una secuencia observable que publica valores durante un período prolongado de tiempo, probarlos en tiempo real puede ser una extensión. La biblioteca de extensiones reactivas proporciona el tipo TestScheduler para ayudar a probar este tipo de código dependiente del tiempo sin esperar realmente a que pase el tiempo. TestScheduler hereda VirtualScheduler y le permite crear, publicar y suscribirse a secuencias en tiempo emulado. Por ejemplo, puede compactar una publicación que tarda 5 días en completarse en una ejecución de 2 minutos, manteniendo la escala correcta. También puede tomar una secuencia que realmente se ha producido en el pasado (por ejemplo, una secuencia de tics de existencias para un año anterior) y calcular o suscribirse a ella como si estuviera insertando nuevos valores en tiempo real.

El método de fábrica Start ejecuta todas las tareas programadas hasta que la cola está vacía, o puede especificar una hora para que las tareas en cola solo se ejecuten en la hora especificada.

En el ejemplo siguiente se crea una secuencia observable activa con las notificaciones OnNext especificadas. A continuación, inicia el programador de pruebas y especifica cuándo suscribirse y eliminar la secuencia observable activa. El método Start devuelve una instancia del ITestableObserver, que contiene una propiedad Messages que registra todas las notificaciones de una lista.

Una vez completada la secuencia, usamos el método ReactiveAssert.AreElementEqual para comparar la propiedad Messages , junto con una lista de valores esperados para ver si ambos son idénticos (con el mismo número de elementos, y los elementos son iguales y en el mismo orden). Al hacerlo, podemos confirmar que realmente hemos recibido las notificaciones que esperamos. En nuestro ejemplo, dado que solo empezamos a suscribirnos a 150, perderemos el valor abc. Sin embargo, cuando comparamos los valores que hemos recibido hasta ahora en 400, observamos que hemos recibido de hecho todos los valores publicados después de suscribirnos a la secuencia. Y también comprobamos que la OnCompleted notificación se desencadenó en el momento correcto en 500. Además, el tipo ITestableObservable devuelto por el método CreateHotObservable también captura la información de suscripción.

De la misma manera, puede usar ReactiveAssert.AreElementsEqual para confirmar que las suscripciones se produjeron en los momentos esperados.

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

Depuración de la aplicación Rx

Puede usar el operador Do para depurar la aplicación Rx. El operador Do permite especificar varias acciones que se deben realizar para cada elemento de secuencia observable (por ejemplo, imprimir o registrar el elemento, etc.). Esto resulta especialmente útil cuando encadenar muchos operadores y desea saber qué valores se generan en cada nivel.

En el ejemplo siguiente, vamos a reutilizar el ejemplo de búfer que genera enteros cada segundo, mientras los coloca en búferes que pueden contener 5 elementos cada uno. En el ejemplo original del tema Querying Observable Sequences using LINQ Operators (Consultar secuencias observables mediante operadores LINQ ), solo se suscribe a la secuencia observable (IList<>) final cuando el búfer está lleno (y antes de vaciarlo). Sin embargo, en este ejemplo, usaremos el operador Do para imprimir los valores cuando la secuencia original los inserte (un entero cada segundo). Cuando el búfer está lleno, usamos el operador Do para imprimir el estado, antes de entregar todo esto como secuencia final para que el observador se suscriba.

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

Como puede ver en este ejemplo, una suscripción se encuentra en el final de una serie de secuencias observables encadenadas. En primer lugar, creamos una secuencia observable de enteros separados por un segundo mediante el operador Interval. A continuación, colocamos 5 elementos en un búfer mediante el operador Buffer y los envíamos como otra secuencia solo cuando el búfer está lleno. Por último, esto se entrega al operador Subscribe. Los datos propagan todas estas secuencias intermedias hasta que se insertan en el observador. De la misma manera, las suscripciones se propagan en la dirección inversa a la secuencia de origen. Al insertar el operador Do en medio de estas propagaciones, puede "espiar" este flujo de datos igual que usa Console.WriteLine en .NET o printf() en C para realizar la depuración.

También puede usar el operador Timestamp para comprobar la hora en que una secuencia observable inserta un elemento. Esto puede ayudarle a solucionar problemas de operaciones basadas en el tiempo para garantizar la precisión. Recuerde el ejemplo siguiente del tema Crear y suscribirse a secuencias observables simples , en el que encadenamos el operador Timestamp a la consulta para que cada valor insertado por la secuencia de origen se anexe cuando se publique. Al hacerlo, cuando se suscribe a esta secuencia de origen, podemos recibir su valor y marca de tiempo.

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 salida debe ser similar a esta:

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

Mediante el uso del operador Timestamp, hemos comprobado que el primer elemento se inserta realmente 5 segundos después de la secuencia y cada elemento se publica 1 segundo más tarde.

Además, también puede establecer puntos de interrupción dentro de expresiones lambda para ayudar en la depuración. Normalmente, solo puede establecer un punto de interrupción para toda la consulta sin descartar un valor determinado para verlo. Para solucionar esta limitación, puede insertar el operador Select en medio de la consulta y establecer un punto de interrupción y, en la instrucción Select, proyectar el valor idéntico como origen mediante una instrucción return en su propia línea. A continuación, puede establecer un punto de interrupción en la return línea de instrucciones y examinar los valores a medida que hacen su camino a través de la consulta.

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

En este ejemplo, el punto de interrupción se establece en la return y línea. Al depurar en el programa, la y variable se muestra en la ventana Variables locales y puede examinar su recuento (5)total. Si expande y, también puede examinar cada elemento de la lista, incluido su valor y tipo.

Como alternativa, puede convertir una expresión lambda en una expresión lambda de instrucción, dar formato al código para que una instrucción esté en su propia línea y, a continuación, establecer un punto de interrupción.

Puede quitar las llamadas Do y Select después de finalizar la depuración.

Consulte también

Conceptos

Crear y suscribirse a secuencias observables simples
Consulta de secuencias observables mediante operadores LINQ
Uso de programadores