0
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をjestでテストする設定方法

Last updated at Posted at 2024-01-31

1.はじめに

create-react-appで作成したappを、jestでテストする環境を作るのに、とても苦労したので、自分のためにも残します。
まだわからないことが多いですが、恥をさらして。:laughing:

試行錯誤してできた環境「jest-prac-app」を元に、もう一度いちからcreate-react-appして「jest-prac-app2」を作ってみたので、名前はそんな感じですが、たぶんほぼ必要十分です。

ソースはこちらです。

1.1.環境・バージョン

  • Windows 10
  • react: 18.2.0
  • create-react-app: 10.2.3
    • npm create-react-app --versionで確認

1.2.ディレクトリ構成

下記のパターン1で、フォルダ名は「test」です。

jest-prac-app
├─ node_modules
├─ public
├─ src
├─ test
└─ package.jsonなどのファイル

2.テスト対象のコンポーネントを準備

2.1.create-react-app

$ npx create-react-app jest-prac-app2
$ cd jest-prac-app2

※ windowsですが、コマンドとわかりやすくするために $ を書いてます。

2.2. コンポーネント作成、配置

2.2.1. MyButton.jsを作成

クリックするとON/OFFが切り替わるボタンです。

さっそくここで、のちのテスト時にハマった点を1つ。1行目のReactは省略できるんですが、入れないとテスト時にエラーになるので入れています。

jest-prac-app/src/MyButton.js
import React, { useState } from "react"

const MyButton = () => {
    const [isOn, setIsOn] = useState(false);

    const handleClick = () => {
        setIsOn((prevState) => !prevState);
    };

    return (
        <button onClick={handleClick}>{isOn?"ON":"OFF"}</button>
    );
}

export default MyButton;

2.2.2. index.jsからAppを外して、代わりにMyButtonを入れる

reportWebVitals関連は、今はいらないので消します。

jest-prac-app/src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import MyButton from './MyButton';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <MyButton />
  </React.StrictMode>
);

2.2.3. 不要ファイルを削除

App.js、App.test.js、App.cssは、放っておいてもいいけど、テストで動いたりするとめんどくさいので、ファイル削除。

2.3. appの動作確認

$ npx run start

ブラウザに出て、デフォルトOFFから、クリックするとON、OFFと切り替わる。htmlはbuttonタグです。
image.png

3. テスト環境の設定<本題>:muscle:

さて、テスト環境。

3.1. package.json

テストをjestで行うために、"test"の行のテスト用コマンドを修正。

jest-prac-app/package.json
{
  "name": "jest-prac-app2",
  ~ 略 ~
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "jest",
    "eject": "react-scripts eject"
  },
  ~ 略 ~
}

3.2. jest.config.jsを作成

  • testMatch:テスト用スクリプトの場所
    • ディレクトリ構成のキモ。
  • moduleNameMapper:importのファイルは、@で始まってたら、rootDirを見てねという設定
    • 正規表現で置換するんですね。()の部分を$1で使う形。
  • setupFilesAfterEnv:最初からある/src/setupTest.jsを参照する設定
    • 入れないと、TypeError: expect(...).toHaveTextContent is not a functionという、関係性がわからない変なエラーが出る。
  • testEnvironmentjsdomは、今はわからない:frowning2:(要調査)
    • とあるエラーが出たときに、:robot:ChatGPTさんに教えてもらった。
jest-prac-app/jest.config.js
module.exports = {
    testMatch: ["<rootDir>/test/**/*.test.js"],
    moduleNameMapper: {
      "^@/(.*)$": "<rootDir>/$1",
    },
    setupFilesAfterEnv: ["<rootDir>/src/setupTests.js"],
    testEnvironment: 'jsdom',
};

3.3. Babel環境の設定

こちらも、今は十分理解できてないです。:frowning2:(要調査)

Babelのプリセットを設定します。(中略)Babelは、ES.nextの構文とJSXを、ブラウザが解釈できるコードに変換します。(O'Reilly Japan - Reactハンズオンラーニング 第2版

$ npm install @babel/preset-env @babel/preset-react

babel.config.jsを作成します。

jest-prac-app/babel.config.js
module.exports = {
    presets: ["@babel/preset-env", "@babel/preset-react"],
};

3.4. @testing-library/user-eventをアップデート

あとで書くテストスクリプト中の、const user = userEvent.setup();で、TypeError: _userEvent.default.setup is not a functionというエラーが出ました。そんなバカなって感じですが、latestを入れれば解決。

どこかで相性問題がおこるかもしれないと目にしたので、/reactもlatestを入れます。

$ npm install @testing-library/react@latest @testing-library/user-event@latest

ちなみにバージョンは、私の環境では下記でした。

jest-prac-app2/package.json(抜粋)(アップデート前)
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
jest-prac-app2/package.json(抜粋)(アップデート後)
    "@testing-library/react": "^14.2.0",
    "@testing-library/user-event": "^14.5.2",

4. テストスクリプトの作成と実施

4.1. テストスクリプトの作成

1行目の、Reactは、コンポーネントと同様、ないとエラーが出ます。

テストの書き方などの詳細は割愛します。なおact()についてまだ理解不足で、おそらく書き方がよくない。でも理解を避けては通れないと感じているところです。

jest-prac-app2/test/MyButton.test.js
import React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { act } from 'react-dom/test-utils';
import MyButton from "@/src/MyButton";

describe("myButtonのテスト", () => {
    test("ボタンをクリックするとON/OFFが切り替わる", async () => {
        // まず描画
        act(() => {
            render(<MyButton />);
        });

        // ユーザーを想定
        const user = userEvent.setup();

        // 初期状態をテスト
        const myButton = screen.getByRole("button");
        expect(myButton).toHaveTextContent("OFF");

        // クリック
        await act(async () => {
            await user.click(myButton);
        });

        // 今度はONになる
        expect(myButton).toHaveTextContent("ON");

    });

    it("itでもいいっていうだけのテスト", async () => {
        // まず描画
        render(<MyButton />);

        // 初期状態をテスト
        const myButton = screen.getByRole("button");
        expect(myButton).toHaveTextContent("OFF");
    });

    test("描画されてすぐはOFFと表示されている", () => {
        // 描画
        const view = render(<MyButton />);

        // スナップショットを取る
        expect(view.container).toMatchSnapshot();
    });
});

4.2. テスト実施

$ npm run test

> jest-prac-app2@0.1.0 test
> jest

 PASS  test/MyButton.test.js
  myButtonのテスト
    √ ボタンをクリックするとON/OFFが切り替わる (174 ms)itでもいいっていうだけのテスト (13 ms)
    √ 描画されてすぐはOFFと表示されている (7 ms)1 snapshot obsolete.
   • MyButtonのテスト スナップショットでテスト 1
Snapshot Summary1 snapshot obsolete from 1 test suite. To remove it, run `npm test -- -u`.
   ↳ test/MyButton.test.js
       • MyButtonのテスト スナップショットでテスト 1

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   1 obsolete, 1 passed, 1 total
Time:        3.171 s
Ran all test suites.

OK!
jest-prac-app2/test/__snapshots__に、スナップショットのファイルもできてました。

5. おわりに

バージョンアップが速いせいかなんかわかりませんが、ググっても正解なんだかそうでないんだかわからないし、聞ける先輩もいない中、試行錯誤で解を得られたことはまずよかったです。何度か諦めかけた・・・。やり始めてからこの記事を書き終えるまで、大体5時間くらいかかりました。

「create-react-app」で道を開拓して、「create-react-app2」できれいな道をもう一度通るという方法でしたが、それでもすんなりとはいかないです。自分が通った道(やったこと)を忘れてるだけなんですが。:joy:

さて。まだ知識不足のところはありつつも、テストの環境はできるようになったっぽいので、テストをエンジョイしたいです。みなさんも、良いテストコーディングを!:sunny:

0
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
0
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?