0
1

react-testing-libraryを使ったテストの始め方

Last updated at Posted at 2023-11-06

準備

// viteでプロジェクト作成
$ npm create vite

// 必要なライブラリのインストール
$ npm install -D jest @types/jest ts-jest jest-environment-jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event

// Jestの設定ファイルの作成 -> jest.config.jsが作成される
$ npx ts-jest config:init
package.json
{
  "name": "react-testing-library",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "test": "jest",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
  "dependencies": {
    "axios": "^1.6.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0"
  },
  "devDependencies": {
    "@testing-library/jest-dom": "^6.1.4",
    "@testing-library/react": "^14.0.0",
    "@testing-library/user-event": "^14.5.1",
    "@types/jest": "^29.5.7",
    "@types/react": "^18.2.15",
    "@types/react-dom": "^18.2.7",
    "@typescript-eslint/eslint-plugin": "^6.0.0",
    "@typescript-eslint/parser": "^6.0.0",
    "@vitejs/plugin-react": "^4.0.3",
    "eslint": "^8.45.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "eslint-plugin-react-refresh": "^0.4.3",
    "jest": "^29.7.0",
    "jest-environment-jsdom": "^29.7.0",
    "ts-jest": "^29.1.1",
    "typescript": "^5.0.2",
    "vite": "^4.4.5"
  }
}
必要ライブラリ

jest.config.jsの追記

  1. 初期設定だとCommonJSの記述方法だが、ESmodulesで記述したいため export default に変更する
  2. 初期設定だとtestEnvironmentがnodeで指定されているが、ブラウザでテストしたいためjest-environment-jsdomに変更する
  3. @testing-library/jest-domのsetupを読み込むためにsetupFilesAfterEnvを指定する
jest.config.js
/** @type {import('ts-jest').JestConfigWithTsJest} */

// module.exports = {
//   preset: 'ts-jest',
//   testEnvironment: 'node',
// };

export default {
  preset: "ts-jest",
  testEnvironment: "jest-environment-jsdom",
  setupFilesAfterEnv: ["./jest-setup.js"],
};
jest-setup.js
import "@testing-library/jest-dom";

tsconfig.jsonの追記

  • プロジェクト内で CommonJSのモジュールの読み込みによる警告アラートを出さないようにする設定
  • 型エラーによるアラートを出さないようにする設定
tsconfig.json
{
  "compilerOptions": {
    "esModuleInterop": true,
    "types": ["@testing-library/jest-dom"]
  }
}

jestを走らせるnpm scriptの追記

  • "test": "jest" を追加 -> npm run test でjestが走る
package.json
{
  "scripts": {
    "dev": "vite",
    "build": "tsc && vite build",
    "test": "jest",
    "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
    "preview": "vite preview"
  },
}

UIテスト

テストの観点

  1. 対象のコンポーネントがレンダリングされているか
  2. 対象のコンポーネントが想定通りの要素を持っているか
  3. 対象のコンポーネントが想定通りのアクション・挙動をするか
ボタンコンポーネントのUIをテストする例
  • テストしたいコンポーネントをインポートする
  • testing-library/reactrender関数 を使ってテスト対象を指定する
  • testing-library/reactscreen関数 を使ってテスト対象がレンダリングされた際のDOMを取得する
  • expect()を使ってテスト内容を記述する
Button.test.tsx
import { render, screen } from "@testing-library/react";

import Button from "./Button";

describe("Button", () => {
  it("ボタンコンポーネントのテスト", () => {
    // render() で実際にレンダリングされたコンポーネントを引数の中に入れる
    render(<Button label="ボタン" onClick={() => alert("click!")} />);

    // screenオブジェクトにはDOMの要素を取得する関数が用意されている -> role属性を取得できるgetByRole()を使ってボタンのDOMを取得する
    const element = screen.getByRole("button");

    // toBeInTheDocument()を使って、DOMが存在するかどうかを確認する
    expect(element).toBeInTheDocument();

    // toHaveTextContent()を使って、DOMのテキストが想定通りかどうかを確認する
    expect(element).toHaveTextContent("ボタン");
  });
});
フォームコンポーネントのUIと動作をテストする例
  • テストしたいコンポーネントをインポートする
  • jest.spyOn を使ってブラウザの挙動をモック化する
  • testing-library/reactrender関数 を使ってテスト対象をレンダリングする
  • testing-library/reactscreen関数 を使ってテスト対象がレンダリングされた際のDOMを取得する
  • @testing-library/user-eventuserEvent オブジェクトを使って、クリックした時の動作を登録する
    • ボタンをクリックする、入力するなどの処理は非同期処理となるため、async/awaitをつける
  • expect()を使ってテスト内容を記述する
  • spyOnを使ってテストをモック化した場合、テストが完了したらspyをクリアする
Button.test.tsx
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

import Form from "./Form";

// ユーザーイベントをテストするために初期設定としてイベントのインスタンスを作成する
const user = userEvent.setup();

describe("Form", () => {
  it("初期状態ではテキストは空欄", () => {
    render(<Form />);

    // inputフォームのDOMを取得する
    const inputEle = screen.getByPlaceholderText("Enter text");

    // toBeInTheDocument()を使って、DOMが存在するかどうかを確認する
    expect(inputEle).toBeInTheDocument();
    // toHaveValue()を使って、DOMのテキストが想定通りかどうかを確認する
    expect(element).toHaveValue("");
  });

  // ユーザーイベントは非同期関数となるためasync/awaitを使ってテストする
  it("入力されたテキストがサブミットされる", async () => {
    /*
      ボタンがクリックされたときwindowアラートを表示させるため、alert()をモック化する
      spyOn関数の第一引数には対象のオブジェクト、第二引数にはモック化する関数を指定する
      mockReturnValue()を使って、モック化した関数の戻り値を指定する
      今回のようにはwindow.alert()は何も返さないため、mockReturnValue()の引数は空となる
    */
    const alertSpy = jest.spyOn(window, "alert").mockReturnValue();

    render(<Form />);

    // submitボタンのDOMを取得する
    const submitEle = screen.getByRole("button");
    // ユーザーがサブミットボタンをクリックする動作を登録する
    // click()の第一引数には対象のDOM要素を指定する
    await user.click(submitEle);
    // alert()が呼ばれて、期待している文字列が表示されたかどうかを確認する
    expect(alertSpy).toHaveBeenCalledWith("submitted: テスト");
    // テストが終わったらspyをクリアする
    alertSpy.mockClear();
  });
});

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