0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Jest】Chakra UIを使用しているプロジェクトでテスト実行時に発生するエラーと対処法

0
Posted at

はじめに

Vite + React + TypeScript + Chakra UIのプロジェクトでJestのテストを実行したところ、Chakra UI特有のエラーが複数発生しました。この記事では、発生したエラーとその対処法をまとめます。

前提

  • Jestとtesting-libraryの導入が完了していること
  • Chakra UIを使用していること

発生したエラーと対処法

エラー①:Providerで囲んでいない

エラー内容

ContextError: useContext returned undefined.
Seems you forgot to wrap component within

原因

実際のアプリでは main.tsx<Provider> がアプリ全体を囲んでいますが、テスト内ではそれがないため、Chakra UIの useContextundefined を返してエラーになります。

対処法

テスト内でmain.tsx と同様に<Provider> でラップすれば解決します。

しかし、テストが増えてくると毎回 <Provider> を書くのが面倒なので、カスタム render を作るのがおすすめです。

src/__tests__/test-utils.tsx を作成:

test-utils.tsx
import { render } from "@testing-library/react";
import { Provider } from "../components/ui/provider";

const customRender = (ui: React.ReactElement) => {
  return render(
    <Provider>{ui}</Provider>
  );
};

export { customRender as render };

テストでは通常の render の代わりにこれをインポートして使います:

App.spec.tsx
import { screen } from "@testing-library/react";
import App from "../App";
import { render } from "./test-utils";

describe("App", () => {
  test("タイトルがあること", () => {
    render(<App />);
    expect(screen.getByText("Hello World")).toBeInTheDocument();
  });
});

test-utils.tsx はテストファイルではないため、jest.config.jstestMatch を設定してテスト対象から除外する必要があります。

jest.config.js
testMatch: ["**/__tests__/**/*.spec.(ts|tsx)"],

エラー②:window.matchMedia is not a function

エラー内容

TypeError: window.matchMedia is not a function

原因

Chakra UIは内部でレスポンシブ対応のために window.matchMedia を使用していますが、Jestのテスト環境(jsdom)には window.matchMedia が存在しないためエラーになります。

対処法

jest.setup.tswindow.matchMedia のモック(ダミー実装)を追加します。

jest.setup.ts
// window.matchMediaのモック
// jsdom環境にはwindow.matchMediaが存在しないため、ダミーの実装を定義する
// Chakra UIなどのUIライブラリがレスポンシブ対応のために内部で使用しているため必要
Object.defineProperty(window, "matchMedia", {
  writable: true,
  value: jest.fn().mockImplementation((query) => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: jest.fn(),
    removeListener: jest.fn(),
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  })),
});

エラー③:structuredClone is not defined

エラー内容

ReferenceError: structuredClone is not defined

原因

Chakra UIが内部で structuredClone を使用していますが、Jestのjsdom環境にはデフォルトで structuredClone が存在しないためエラーになります。

対処法

jest.setup.ts にポリフィル(代替実装)を追加します。

jest.setup.ts
// structuredCloneのポリフィル(代替実装)
// jsdom環境にはstructuredCloneが存在しないため、JSON変換で代用する
// Chakra UIなどのライブラリが内部でstructuredCloneを使っている場合に必要
if (typeof structuredClone === "undefined") {
  global.structuredClone = (val: any) => JSON.parse(JSON.stringify(val));
}

structuredClone が存在しない場合のみ、JSON.parse(JSON.stringify(...)) で代用する簡易的なポリフィルです。

jest.setup.ts の全体像

上記の対処法をすべて入れた jest.setup.ts はこうなります。

jest.setup.ts
import "@testing-library/jest-dom";
import { config } from "dotenv";

config({ quiet: true });

if (typeof structuredClone === "undefined") {
  global.structuredClone = (val: any) => JSON.parse(JSON.stringify(val));
}

Object.defineProperty(window, "matchMedia", {
  writable: true,
  value: jest.fn().mockImplementation((query) => ({
    matches: false,
    media: query,
    onchange: null,
    addListener: jest.fn(),
    removeListener: jest.fn(),
    addEventListener: jest.fn(),
    removeEventListener: jest.fn(),
    dispatchEvent: jest.fn(),
  })),
});
0
1
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
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?