Skip to content

Introducción al Marble Testing

RxJS proporciona una técnica llamada "marble testing" que permite probar el comportamiento de streams asíncronos con una representación visual.

En esta sección, aprenderemos los fundamentos del marble testing a través de ejemplos simples.

¿Qué es la Notación de Marble?

La notación de marble es una forma de representar el paso del tiempo y la ocurrencia de eventos con strings.

Reglas básicas

Estos símbolos se usan para representar el paso del tiempo y la ocurrencia de eventos.

SímboloSignificado
-El tiempo pasa (avanzar 1 frame)
a, b, cValores emitidos (caracteres arbitrarios)
``
#Error

Por ejemplo.

text
--a-b--c-|

Esto significa:

  • Esperar 2 frames y a es emitido
  • b después de 1 frame
  • c 2 frames después
  • Completar después de 1 frame más

Diferencia entre Cold y Hot

Cold Observable

Cold Observable es "reproducido desde el principio con cada suscripción".

ts
import { TestScheduler } from 'rxjs/testing';
import { describe, it, beforeEach, expect } from 'vitest';

describe('Fundamentos de TestScheduler', () => {
  let testScheduler: TestScheduler;

  beforeEach(() => {
    testScheduler = new TestScheduler((actual, expected) => {
      expect(actual).toEqual(expected);
    });
  });

  it('Prueba simple', () => {
    testScheduler.run(({ cold, expectObservable }) => {
      const source$ = cold('--a--b--c|');

      expectObservable(source$).toBe('--a--b--c|');
    });
  });
});

Hot Observable

Un Hot Observable es un stream que "ya está en progreso". Si se suscribe a mitad del stream, solo recibirá valores desde ese punto en adelante.

ts
import { TestScheduler } from 'rxjs/testing';
import { describe, it, beforeEach, expect } from 'vitest';

describe('Fundamentos de TestScheduler', () => {
  let testScheduler: TestScheduler;

  beforeEach(() => {
    testScheduler = new TestScheduler((actual, expected) => {
      expect(actual).toEqual(expected);
    });
  });

  it('Prueba simple', () => {
    testScheduler.run(({ hot, expectObservable }) => {
      const source$ = hot('--a--b--c|');

      expectObservable(source$, '----^').toBe('-----b--c|');
    });
  });
});

Un ejemplo simple de marble test

Por ejemplo, para probar el operador debounceTime

ts
import { debounceTime } from 'rxjs';
import { TestScheduler } from 'rxjs/testing';
import { describe, it, beforeEach, expect } from 'vitest';

describe('Fundamentos de TestScheduler', () => {
  let testScheduler: TestScheduler;

  beforeEach(() => {
    testScheduler = new TestScheduler((actual, expected) => {
      expect(actual).toEqual(expected);
    });
  });

  it('Prueba simple', () => {
    testScheduler.run(({ cold, expectObservable }) => {
      const source$ = cold('--a--b----c|');
      const result$ = source$.pipe(
        debounceTime(20)
      );

      const expected =    '-----------(c|)';

      expectObservable(result$).toBe(expected, { c: 'c' });
    });
  });
});

Aquí verificamos que solo la última c emitida es la salida.

Notas

  • Un carácter en la notación de marble representa un frame (10ms) por defecto (configurable dependiendo del entorno)
  • Los operadores dependientes del tiempo como debounceTime, delay, interval, etc. funcionan bien con marble tests
  • Use expectObservable para validar la salida del stream
  • expectSubscriptions es una característica avanzada que valida el tiempo de las suscripciones, pero no se cubre aquí

Resumen

Marble testing es una técnica muy poderosa que hace que las pruebas de código RxJS sean visibles e intuitivas.

  • Ser consciente de la diferencia entre Cold y Hot
  • Representar el paso del tiempo y eventos como strings
  • Los streams asíncronos complejos también se pueden probar claramente

¡Comience con un marble test simple para practicar!

Publicado bajo licencia CC-BY-4.0.