👻

Jestで即時関数を含むコードをテストする

2024/11/24に公開

Jestで即時関数を含むソースファイルをテストする際に、そのままではエラーになってしまってテストできないものがありました。

前提条件

  • Jest
  • TypeScript

テスト対象のコード

テスト対象のコードは、テスト対象のコードと即時関数が同じファイルに書かれています。

sample.ts
// テスト対象のコード

// 即時関数(テスト対象ではない)
(() => {
  const button = document.getElementById('button');
  button.classList.add('active');
})();

// テストしたい関数
export function sampleTest(): string {
  return 'Hello, World!';
}

テストコード

エラーになるケース

テストコードではJestを用いて関数の返り値をテストします。

sample.test.ts
import { sampleTest } from 'path/to/sample';

describe('sample', () => {
  it('sample', () => {
    const res = sampleTest();
    expect(res).toBe('Hello, World!');
  });
});

上記のテストを実行すると、テスト対象の関数だけでなく即時関数も実行されてしまい、処理によってはエラーになってしまいます。

 FAIL  path/to/sample.test.ts
  ● Test suite failed to run

    TypeError: Cannot read properties of null (reading 'classList')

      1 | (() => {
      2 |   const button = document.getElementById('button');
    > 3 |   button.classList.add('active');
        |          ^
      4 | })();
      5 |
      6 | export function sampleTest(): string {

      at classList (path/to/sample.ts:3:10)
      at Object.<anonymous> (path/to/sample.ts:4:2)
      at Object.<anonymous> (path/to/sample.test.ts:7:1)

Test Suites: 1 failed, 1 total
Tests:       0 total
Snapshots:   0 total
Time:        3.93 s
Ran all test suites within paths "path/to/sample.test.ts".

解決方法

エラーを解決するには即時関数内で実行される処理をモックすればOKです。ただし、テストコード内での記述場所が大切で、import文よりも前に書く必要があります。

sample.test.ts
// 即時関数内の処理をモック
jest.spyOn(document, 'getElementById').mockReturnValue({
  classList: {
    add: jest.fn(),
  },
} as unknown as HTMLElement);

// モックよりも後にimportする必要がある
import { sampleTest } from 'path/to/sample';

describe('sample', () => {
  it('sample', () => {
    const res = sampleTest();
    expect(res).toBe('Hello, World!');
  });
});

まとめ

即時関数を含むソースファイルをJestでテストする場合の注意点を記載しました。テストコードでは、テスト対象のimport文よりも前に即時関数内の処理をモックする必要があります。

Discussion