TestSchedulerを活用したテスト 
RxJSのTestSchedulerは、時間ベースのオペレーターを正確にテストするための強力なツールです。この章では、TestSchedulerを活用したテスト方法を体系的に解説します。
TestSchedulerとは? 
通常、Observableは時間に依存して動作します。たとえば、delay()やdebounceTime()は一定時間待つオペレーターです。
 テストにおいて実際に待つのは非効率なため、仮想時間を使って即座にテストを行う仕組みがTestSchedulerです。
TIP
TestSchedulerでは「仮想時間」を使うため、リアル時間の待機が不要になります。
TestSchedulerの基本構成 
ts
import { TestScheduler } from 'rxjs/testing';
import { describe, it, beforeEach, expect } from 'vitest';
describe('TestSchedulerの基本', () => {
  let testScheduler: TestScheduler;
  beforeEach(() => {
    testScheduler = new TestScheduler((actual, expected) => {
      expect(actual).toEqual(expected);
    });
  });
  it('簡単なテスト', () => {
    testScheduler.run(({ cold, expectObservable }) => {
      const source$ = cold('--a--b--c|');
      const expected =  '--a--b--c|';
      expectObservable(source$).toBe(expected);
    });
  });
});cold():購読ごとに独立してストリームが始まるCold Observableを作成hot():ストリームがすでに進行しているHot Observableを作成expectObservable():Observableの出力をマーブル記法で検証
Cold ObservableとHot Observable 
| 種類 | 特徴 | 用途 | 
|---|---|---|
| Cold Observable | 購読のたびに最初からデータを流す | HTTPリクエストなど | 
| Hot Observable | データの流れがすでに始まっていて購読者に共有される | ユーザーイベント、WebSocketなど | 
マーブル記法の基本 
マーブル記法とは、Observableの時間経過を文字列で表す手法です。
| 記号 | 意味 | 
|---|---|
- | 時間の経過(1フレーム) | 
a, b, c | 発行される値 | 
| ` | ` | 
# | エラー | 
()  | 複数の値を同時に発行(複数イベント) | 
例 
--a--b--c|    // 2フレーム後にa、その後b、c、完了仮想時間を使ったテスト例 
debounceTimeのテスト 
ts
import { describe, it, expect, beforeEach } from 'vitest';
import { debounceTime, map } from 'rxjs';
import { TestScheduler } from 'rxjs/testing';
describe('仮想時間を使ったテスト', () => {
  let testScheduler: TestScheduler;
  beforeEach(() => {
    testScheduler = new TestScheduler((actual, expected) => {
      expect(actual).toEqual(expected);
    });
  });
  it('debounceTimeオペレーターのテスト', () => {
    testScheduler.run(({ cold, expectObservable }) => {
      const source$ = cold('--a--b----c|');
      const result$ = source$.pipe(
        debounceTime(20),
        map(x => x.toUpperCase())
      );
      const expected =    '-----------(C|)';  // ←これ!
      expectObservable(result$).toBe(expected, { B: 'B', C: 'C' });
    });
  });
});エラー処理のテスト 
ts
import { describe, it, expect, beforeEach } from 'vitest';
import { catchError} from 'rxjs';
import { TestScheduler } from 'rxjs/testing';
import { of } from 'rxjs';
describe('エラー処理のテスト', () => {
  let testScheduler: TestScheduler;
  beforeEach(() => {
    testScheduler = new TestScheduler((actual, expected) => {
      expect(actual).toEqual(expected);
    });
  });
  it('catchErrorでエラー補足する', () => {
    testScheduler.run(({ cold, expectObservable }) => {
      const source$ = cold('--a--#');
      const result$ = source$.pipe(
        catchError(() => of('X'))
      );
  
      const expected =    '--a--(X|)';
  
      expectObservable(result$).toBe(expected);
    });
  });
});まとめ 
- TestSchedulerを使うとリアル時間を待たずにテスト可能
 - Cold/Hot Observableの違いを理解して使い分ける
 - マーブル記法を駆使して時間経過を可視化する
 - 複雑な非同期ストリームも精密にテストできる
 
[!NEXT] 次は、さらに高度なマーブルテスト(マーブル文字列のカスタマイズや複数ストリームの組み合わせ)について学びます。