Sassyブログ

埼玉県在住のシステムエンジニアです。多ジャンルなブログですが、基本的にはIT関連の内容を中心に他のちょいちょい他ジャンルの記事も発信していきます。

Jestでイベントリスナーのコールバック関数を検証する

画面のテストコードを書いている時に、画面を閉じた時やスリープした時に発生するイベントのコールバック関数の動作をテストしたい時があるかと思います。

そんな時にどうテストを書いたら良いかを以下にサンプルコードを交えながら備忘録として残していきます。

技術 ・React ・ReactTestingLibrary ・jest

まずは以下のような実装があるとします。

・・・省略

const handleVisibilitychange = useCallback(() => {
    if (document.visibilityState === ‘hidden’) {
      if (!ref.current) {
        return;
      }
      ref.current.pause()
    }
}, []);

useEffect(() => {
    window.addEventListener(‘visibilitychange’, handleVisibilitychange, false);
    return () => {
      window.removeEventListener(‘visibilitychange’, handleVisibilitychange, false);
    };
}, [handleVisibilitychange]);

・・・省略

コンテンツが隠れたり、PCのウィンドウがスリープ等によりユーザーから隠れた時に呼ばれるvisibilitychangeイベントのリスナーを設定します。

コールバック関数の実装としてはvisibilityStatehiddenであれば動画を位置停止するという簡単な処理です。

これに対してコンテンツが隠れた時にpause関数が呼ばれていることをテストしたいとします。

その時は以下のようにかきます。

test(‘動画再生中に画面が非表示となった場合は一時停止すること’, async () => {
  const mockPause = jest.spyOn(window.HTMLMediaElement.prototype, ‘pause’).mockImplementation(() => {});

  Reflect.defineProperty(document, ‘visibilityState’, {
    value: ‘hidden’,
    writable: true,
  });

  await setup(); // 省略していますがテスト対象のページをレンダリングしてます。

  act(() => {
    window.dispatchEvent(new Event(‘visibilitychange’));
  });

  expect(mockPause).toHaveBeenCalled();
});

まずpause関数をモック化します。

const mockPause = jest.spyOn(window.HTMLMediaElement.prototype, ‘pause’).mockImplementation(() => {});

その次にdocumentオブジェクトにvisibilityStateプロパティを追加します

jest上でのJS実行はブラウザで実行しているわけではないのでwindowオブジェクトやdocumentオブジェクトには画面に関する情報は含まれません。

そのため、テストコードの中であらかじめモックとして仕込んで上げる必要があります。

Reflect.defineProperty(document, ‘visibilityState’, {
  value: ‘hidden’,
  writable: true,
});

次にwindow.dispatchEventを使ってイベントをエミュレートします。

ReactTestingLibraryにはfireEventというユーザー操作をエミュレートする関数を持っているオブジェクトがあるのですが、visibilitychangeを発生させる関数を持っていなかったのでwindow.dispatchEventを使用してます。

ここでactでイベント発生処理をラップしてあげることで、イベント発生が完了して更新処理等が完了してから検証するようにします。

actでラップしないとイベント発生直後間髪入れずに検証が行われてしまうことでmockPauseの情報が更新される前にexpectが実行されるのでテストに失敗してしまいます。

なのでこのように何かしらの処理を行って検証対象のデータが反映されてから検証したい場合はactで囲みます。

act(() => {
  window.dispatchEvent(new Event(‘visibilitychange’));
});

expect(mockPause).toHaveBeenCalled();

このようにテストすることでイベントリスナーのコールバック関数のテストを行うことが可能になります。