2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

どうやってReact Function ComponentをMockするか

Posted at

検証環境

Jest

React Testing Library

Create React App(上記が含まれるので)

背景

サードパーティ製のComponentを避けてテストを行うために、ComponentをMockする必要がありました。

特に、Amplify-uiなど事前設定が必要なComponentに対しては有効な手段となります。

テスト環境を作成

# 作業フォルダを作成
mkdir work && cd work

# Package ManagerでCRAを利用してReact Applicationを生成する
pnpm create react-app jest-app --template typescript

# フォルダに移動
cd jest-app

# componentsとtestフォルダを作成
mkdir src/components src/tests

# Editorを開く
code .

Componentを作成

以下をサードパーティ製のComponentやテスト範囲外の"複雑なComponent"と仮定します。

src/components/complex-component.tsx
export default function ComplexDefaultComponent() {
  return <div>Default exported complex component</div>;
}

export function ComplexNamedFunction() {
  return <div>Named exported complex component</div>;
}

以下がテスト対象のComponentです。上記の"複雑なComponent"を用いて構成されています。

src/components/parent-component.tsx
import ComplexDefaultComponent, { ComplexNamedFunction } from './complex-component';

export default function ParentComponent() {
  return (
    <div>
      <ComplexDefaultComponent />
      <ComplexNamedFunction />
      <div>Parent component</div>
    </div>
  );
}

Mock無しのテストをまず生成します。

src/tests/parent-component.test.tsx
import { render, screen } from '@testing-library/react';
import ParentComponent from '../components/parent-component';

test('ParentComponent', () => {
  render(<ParentComponent />);

  screen.debug();

  // Mock無し場合は、当然複雑なComponentがそのまま表示されます。
  expect(screen.getByText('Default exported complex component')).toBeInTheDocument();
  expect(screen.getByText('Named exported complex component')).toBeInTheDocument();
});

screen.debug()
    <body>
      <div>
        <div>
          <div>
            Default exported complex component
          </div>
          <div>
            Named exported complex component
          </div>
          <div>
            Parent component
          </div>
        </div>
      </div>
    </body>

実際にMockしてみる

jest.mock()を使用します。
import * as xxx from 'xxx'とすれば、jest.sypOn()でも可能ですが、手間が増えるので採用しません。

Default exportとNamed exportのMockのされ方に注意してください。
以下のようにテストファイルを書き換えます。Mockされた内容でテストは成功します。

src/tests/parent-component.test.tsx

import { render, screen } from '@testing-library/react';
import ParentComponent from '../components/parent-component';

// Jest内でHoistingされるので、jest.mockはModuleスコープで宣言する必要があります
jest.mock('../components/complex-component', () => {
  return {
    // Default exportされるObjectを明示的にexportするためのdフラグ
    __esModule: true,
    // Default export
    default: () => <div>Mock default export</div>,
    // Named export
    ComplexNamedFunction: () => <div>Mock named export</div>,
  };
});

test('ParentComponent', () => {
  render(<ParentComponent />);

  screen.debug();

  // 本来のComponentは表示されません
  expect(screen.queryByText('Default exported complex component')).not.toBeInTheDocument();
  expect(screen.queryByText('Named exported complex component')).not.toBeInTheDocument();

  // MockされたComponentが表示されます
  expect(screen.getByText('Mock default export')).toBeInTheDocument();
  expect(screen.getByText('Mock named export')).toBeInTheDocument();
});

もちろん中身もMockされていますね。

screen.debug()
    <body>
      <div>
        <div>
          <div>
            Mock default export
          </div>
          <div>
            Mock named export
          </div>
          <div>
            Parent component
          </div>
        </div>
      </div>
    </body>

最後に

上記のようにMockすることで、テスト範囲を限定し、効果的なテストを行うことが可能です。

ちなみに記事によっては、jest.mock('xxx', () => () => {return <div>ooo</div>})のように、defaultNamed exportを明示せずともMockできるとありましたが、私の環境ではエラーが発生し、うまくいきませんでした。

テストコードは、コードレビューやモジュール分離などのヒントも与えてくれます。ドキュメント上のテストの仕組みだけでなく、実際に手を動かすことで見えてくるものがあるので、これからも自分に負けずにテストを書いていきたいです。

参考

公式のjest.mock()

この方法は可能?

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?