こんにちは、かつコーチです。
前回までは認証・セキュリティ編として、Protected RouteやXSS対策を扱ってきました。
安全なアプリが作れるようになったら、次に意識したいのが「そのコードが本当に正しく動いているか」を保証する仕組みです。
ここからは数回に分けて、Reactアプリの「テスト」について解説していきます。
初回の今回は、テストの土台となるJestというテストフレームワークの基本を押さえていきましょう。
Jestとは?
JavaScript・TypeScript向けのテストフレームワーク
Jestは、Meta社が開発しているJavaScript・TypeScript向けのテストフレームワークです。
「関数に特定の値を渡したら、期待した結果が返ってくるか」を自動的に検証してくれるツールだと考えてください。
Create React AppやVite + Reactの構成でも広く採用されており、Reactのテストといえばまず名前が挙がる定番の選択肢です。
なぜテストを書く必要があるのか
「動いているんだから、わざわざテストを書かなくてもいいのでは?」と感じる人も多いと思います。
私自身、最初はそう思っていた一人でした。
しかし、実際に機能追加や修正を重ねていくと、「あるページを直したら、別のページが壊れていた」という経験を何度もすることになります。
テストコードがあれば、コードを変更したときに「今まで動いていた部分が壊れていないか」を自動的にチェックできます。
これを回帰テスト(デグレ防止)と呼び、開発が進むほどその価値を実感するようになります。
テストの種類
単体テスト・結合テスト・E2Eテストの違い
Reactアプリのテストには、大きく分けて次のような種類があります。
| 種類 | 対象 | 目的 |
|---|---|---|
| 単体テスト(ユニットテスト) | 関数・コンポーネント単体 | 個々の処理が正しく動くか確認する |
| 結合テスト | 複数のコンポーネントの組み合わせ | 連携部分が正しく動くか確認する |
| E2Eテスト | アプリ全体(実際のブラウザ操作) | ユーザー視点で一連の操作が成立するか確認する |
Jestは、このうち主に単体テスト・結合テストを担当するツールです。
E2Eテストについては、このシリーズの後半で別のツールを使って扱う予定なので、今はJestが「細かい単位のテストを担当する道具」だと理解しておけば十分です。
Jestの環境構築
インストール手順
Vite + React + TypeScriptのプロジェクトを前提に、必要なパッケージをインストールします。
npm install --save-dev jest @types/jest ts-jest
TypeScriptのコードをそのままテストできるように、ts-jestも一緒に入れておきます。
設定ファイルの作成
jest.config.tsを作成し、TypeScript用の設定を記述します。
// jest.config.ts
import type { Config } from "jest";
const config: Config = {
preset: "ts-jest",
testEnvironment: "jsdom",
testMatch: ["**/*.test.ts", "**/*.test.tsx"],
};
export default config;
ここでのポイントはtestEnvironment: "jsdom"です。
Reactのコンポーネントはブラウザ上のDOMを前提にしているため、Node.js環境だけではテストを実行できません。
jsdomは、Node.js上で疑似的にブラウザのDOM環境を再現してくれるライブラリで、これを指定することでコンポーネントのテストが可能になります。
package.jsonにスクリプトを追加
テストを毎回コマンドを打たずに実行できるよう、package.jsonにスクリプトを追加しておきます。
{
"scripts": {
"test": "jest"
}
}
これでnpm testと打つだけでテストが実行できるようになります。
Jestの基本文法
describeとit(test)
Jestのテストコードは、describeとit(もしくはtest)という2つの関数を軸に組み立てます。
// sum.test.ts
function sum(a: number, b: number): number {
return a + b;
}
describe("sum関数", () => {
it("2つの数値を足し算できる", () => {
expect(sum(1, 2)).toBe(3);
});
it("マイナスの数値でも計算できる", () => {
expect(sum(-1, -2)).toBe(-3);
});
});
describeはテストのグループ(まとまり)を表し、itが個々のテストケースを表します。
expect(実際の値).toBe(期待する値)という形で、「この値はこうなるはずだ」というアサーション(検証)を書きます。
よく使うマッチャー
toBe以外にも、目的に応じてさまざまなマッチャー(検証方法)が用意されています。
test("よく使うマッチャーの例", () => {
expect(1 + 1).toBe(2); // 完全一致
expect({ name: "かつコーチ" }).toEqual({ name: "かつコーチ" }); // オブジェクトの中身が一致
expect([1, 2, 3]).toContain(2); // 配列に含まれるか
expect(null).toBeNull(); // nullかどうか
expect(() => {
throw new Error("エラー");
}).toThrow(); // エラーが発生するか
});
toBeはプリミティブな値の比較に、toEqualはオブジェクトや配列の中身の比較に使う、という使い分けを覚えておくと迷いにくくなります。
つまずきやすいポイント:toBeとtoEqualの混同
オブジェクトの比較で失敗するケース
私が初めてJestに触れたとき、最初につまずいたのがこのtoBeとtoEqualの違いでした。
❌ Before:オブジェクトの比較にtoBeを使ってしまう
test("ユーザー情報オブジェクトの比較", () => {
const user = { name: "かつコーチ", age: 30 };
expect(user).toBe({ name: "かつコーチ", age: 30 });
// テスト失敗:中身は同じでも別のオブジェクトとして扱われる
});
toBeはObject.isと同じ比較方法を使うため、中身が同じでも参照先が異なる別のオブジェクトは「一致しない」と判定されます。
見た目は同じデータなのになぜかテストが落ちる、という状況に最初は戸惑いました。
✅ After:オブジェクトの比較にはtoEqualを使う
test("ユーザー情報オブジェクトの比較", () => {
const user = { name: "かつコーチ", age: 30 };
expect(user).toEqual({ name: "かつコーチ", age: 30 });
// テスト成功:中身(プロパティの値)が一致していればOK
});
toEqualは値の中身を再帰的に比較してくれるため、オブジェクトや配列を検証するときはこちらを使うのが基本です。
「プリミティブ値ならtoBe、オブジェクトや配列ならtoEqual」というルールを覚えておくだけで、このつまずきは回避できます。
まとめ
この記事のポイント
- JestはMeta社製のJavaScript・TypeScript向けテストフレームワーク
- テストコードは、コードを変更したときの「デグレ防止」に役立つ
- Reactコンポーネントのテストには
testEnvironment: "jsdom"の設定が必要 describeでグループ化し、it(test)で個々の検証を書く- プリミティブ値の比較は
toBe、オブジェクトや配列の比較はtoEqualを使う
次に読むべき記事
Jestの基本文法が分かったところで、次はReactコンポーネントを実際にテストするための専用ライブラリ、React Testing Libraryについて学んでいきましょう。
→ 次の記事:React Testing Library入門