Skip to content

Introduction au marble testing

Dans RxJS, une méthode appelée "marble testing" est disponible pour tester le comportement des flux asynchrones tout en les représentant visuellement.

Ici, nous apprendrons les bases du marble testing à travers des exemples simples.

Qu'est-ce que la notation marble ?

La notation marble est une méthode pour représenter le passage du temps et l'occurrence d'événements avec une chaîne de caractères.

Règles de base

SymboleSignification
-Passage du temps (avance d'1 frame)
a, b, cValeur émise (caractère arbitraire)
``
#Erreur (error)

Par exemple

text
--a-b--c-|

Cela signifie :

  • Attend 2 frames puis émet a
  • 1 frame plus tard, émet b
  • 2 frames plus tard, émet c
  • 1 frame de plus puis se termine

Différence entre Cold et Hot

Cold Observable

Les Cold Observables "rejouent depuis le début pour chaque souscription".

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

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

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

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

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

Hot Observable

Les Hot Observables sont des flux "déjà en cours". Lors d'une souscription en cours de route, seules les valeurs à partir de ce moment peuvent être reçues.

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

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

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

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

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

Exemple simple de marble testing

Par exemple, pour tester l'opérateur debounceTime

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

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

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

  it('test 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' });
    });
  });
});

Ici, nous vérifions que seul le c émis en dernier est produit en sortie.

Points d'attention

  • Un caractère dans la notation marble représente par défaut 1 frame (10ms) (configurable selon l'environnement)
  • Les opérateurs dépendants du temps comme debounceTime, delay, interval sont très compatibles avec le marble testing
  • Utilisez expectObservable pour vérifier la sortie du flux
  • expectSubscriptions est une fonctionnalité avancée pour vérifier le timing des souscriptions, mais n'est pas couverte ici

Résumé

Le marble testing est une méthode très puissante qui permet de visualiser et comprendre intuitivement les tests de code RxJS.

  • Être conscient de la différence entre Cold et Hot
  • Représenter le passage du temps et les événements avec des chaînes de caractères
  • Des tests clairs deviennent possibles même pour des flux asynchrones complexes

Commençons par pratiquer avec des marble tests simples !

Publié sous licence CC-BY-4.0.