jest で孫モジュールだけモックしたい!

2022/09/04に公開

例えば: React のコンポーネントのテストで、そのコンポーネントが使っているモジュールをモックしたいときなど

解決方法

各種モジュール(@testing-library/reactreact など)を再インポートする

実例

import { render } from '@testing-library/react';
import { Component } from './Component';

describe('Component', () => {
  test('without mock', () => {
    render(<Component />);
  });
  jest.isolateModules(() => {
    const { render } = require('@testing-library/react'); // 再インポートする
    jest.mock('./dateModule', () => ({ now: () => 42 }));

    const { Component } = require('./Component');
    test('mocking module that Component uses', () => {
      render(<Component />);
    });
    jest.unmock('./dateModule');
  });
  jest.isolateModules(() => {
    const { render } = require('@testing-library/react'); // 再インポートする
    jest.mock('./dateModule', () => ({ now: () => 42 }));

    const { Component } = require('./Component');
    test('mocking module that Component uses', () => {
      render(<Component />);
    });
    jest.unmock('./dateModule');
  });
});

react-testing-library を 再 import しないと、Component が読み込んでいる react と react-testing-library でズレが生じて、You might have more than one copy of React とエラーが出るはずです。

jest.isolateModules とは

jest は 各モジュールをキャッシュしています。つまり、require した時に同じオブジェクトが返ってきます。

expect(require('./moduleA')).toBe(require('./moduleA')); // どのモジュールでも成立する

isolateModules は、新しいモジュールのキャッシュを持ったサンドボックスを生成します

import { Component } from './Component';
import React from 'react';

test('check equality',() => {
  expect(Component).toBe(require('./Component').Component); // 等しい
  expect(React).toBe(require('react')); // 等しい

  jest.isolateModules(() => {
    expect(Component).not.toBe(require('./Component').Component); // 等しくない
    expect(React).not.toBe(require('react')); // 等しくない
  });
});

Discussion