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?

Jest でテストを書く時のポイント

Posted at

1. 基本原則

テストは「実装」ではなく、「振る舞い」を検証する

  • OK: 「この入力を与えたら、この結果になる」
  • NG: 「この関数の中でこの関数が呼ばれている」

実装詳細はリファクタで変わりますが、振る舞いは仕様です。

2. 優先順位(何から書くべきか)

  1.  ビジネスロジック 最重要!
    壊れると業務に直結する箇所。

    (例)

    • 金額計算
    • 集計ロジック
    • フラグによる分岐
       
  2. 条件分岐・境界値
    バグが入りやすい箇所。

    (チェックすべき例)

    • null / undefined
    • 0 / 1
    • 空配列 / 1件 / 複数件
    • 締日またぎ、月跨ぎ
       
  3. API・Fetcher の振る舞い
    外部依存は モックして結果のみ検証。
     

  4. 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」で分離する
 
 

💡💡💡
テストは「実装を守る」ものではなく、「仕様を守る」ために書く

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?