1. 基本原則
テストは「実装」ではなく、「振る舞い」を検証する
- OK: 「この入力を与えたら、この結果になる」
- NG: 「この関数の中でこの関数が呼ばれている」
実装詳細はリファクタで変わりますが、振る舞いは仕様です。
2. 優先順位(何から書くべきか)
-
ビジネスロジック 最重要!
壊れると業務に直結する箇所。(例)
- 金額計算
- 集計ロジック
- フラグによる分岐
-
条件分岐・境界値
バグが入りやすい箇所。(チェックすべき例)
- null / undefined
- 0 / 1
- 空配列 / 1件 / 複数件
- 締日またぎ、月跨ぎ
-
API・Fetcher の振る舞い
外部依存は モックして結果のみ検証。
-
React コンポーネント(最小限)
UI は「見た目」ではなく ユーザー操作に対する結果を見る。(チェックすべき例)
- 表示される/されない
- ボタン押下で何が起きるか
- バリデーション表示
3. Jest で「書かなくていい」テスト
実務ではここを削るのが重要。
❌ getter / setter
❌ 単純な型変換だけの関数
❌ MUI / React Hook Form の内部挙動
❌ 「実装と同じロジックをテストで再現」
4. describe / test の書き方の型
describe:前提条件
test:期待結果
describe("締日が月末の場合", () => {
test("2月でも正しく月末日を返す", () => {
expect(getClosingDate("2025-02")).toBe("2025-02-28");
});
});
5. testを書く際に問題を切り出す。
const handleSearch = async (formData) => {
if (loading) return;
setRows([]);
setForm(formData);
setLoading(true);
try {
const result = await fetchData(...);
const normalized = result.map((r) => ({
...structuredClone(r),
isEdited: false,
}));
} catch {
setRows([]);
} finally {
setLoading(false);
}
};
上記コードは、Reactの State 更新と、use-case が混ざっているので分離する。
export const normalizeRows = (
rows: Rows[]
): Rows[] => {
return rows.map((r) => ({
...structuredClone(r),
isEdited: false,
}));
};
export const searchData = async (
formData: FormData
) => {
const result = await fetchData({
formData,
maxConcurrency: 5,
});
const normalized = normalizeRows(result);
return {
rows: normalized,
initialRows: normalized.map((r) => structuredClone(r)),
};
};
純粋関数を切り出した後のReact側。
React側は結果を受け取るだけ。
const handleSearch = async (formData) => {
if (loading) return;
setLoading(true);
setForm(formData);
setRows([]);
try {
const { rows, initialRows } =
await searchData(formData);
setRows(rows);
setInitialRows(initialRows);
} catch (e) {
console.error("検索エラー:", e);
setRows([]);
} finally {
setLoading(false);
}
};
Reactがないと動かないものは、Jest で無理に単体テストをしなくてもいい。
+ α ユースケース(use-case) とは
use-case とは ユーザー操作単位の業務処理です。
use-case は「1ユーザー操作 = 1関数」が基本
- 検索 →
searchXXX - 保存 →
saveXXX - クリア →
clearXXX(不要なことも多い) - 登録 →
createXXX - 更新 →
updateXXX
あるボタンを押したら、
- どの API を呼ぶか
- 失敗したらどう扱うか
- 結果をどう加工するか
業務として正しい形にする、「アプリの判断、流れ」が1つの use-case の塊。
👉 なぜ use-caseを切り出すのか?
理由① Jest でテストしやすい
- React 不要 (React と API の間に置く)
- 入力 → 出力 が明確
- API はモックできる
理由② 仕様が見える
6. まとめ
Jestでテストを書く時のポイント
💡 Jestの単体テストは、業務ロジックが対象
● 判断(if / switch)
● 計算(金額・日付)
● 整形(map / filter / diff)
💡 React(UI)と業務ロジックは切り離す
💡 業務ロジックは「1ユーザー操作 = 1 use-case」で分離する
💡💡💡
テストは「実装を守る」ものではなく、「仕様を守る」ために書く