メインコンテンツへ移動
SEMentor

テスト設計の考え方——書きたいテストではなく、価値のあるテストを書く

2026年4月26日 約7分で読める

カバレッジ100%でも品質が高いとは限らない。テストの目的を正しく理解し、守るべき挙動を守り切るテスト設計の考え方を解説する

テストを「書いた」だけでは意味がない

テストを書くことへの抵抗がなくなってきた次の段階で、多くのエンジニアが別の壁にぶつかります。

  • テストコードが増えたのに、バグが減らない
  • カバレッジは高いのに、リリースのたびに不具合が出る
  • テストのメンテナンスが大変すぎて、コードを変えるのが怖い

これらの問題に共通するのは、「何のためにテストを書くのか」が曖昧なまま、とりあえずテストを増やしていることです。

テストは量ではなく、設計で品質が決まります。

テストの3つの目的

テストには3つの目的があります。どれか一つではなく、バランスを意識することが重要です。

1. 仕様の明文化

テストは「この入力に対してこの出力を期待する」という仕様書です。新しいメンバーがコードを読んだとき、テストがあれば意図を理解しやすくなります。

2. 変更に対する安全網

コードを変更したとき、意図しない副作用がないかを確認します。リファクタリングや機能追加で既存の動作を壊していないかを即座に検知できます。

3. 設計へのフィードバック

テストを書きにくいコードは、設計に問題があるサインです。「このクラスをテストするには多数の依存を用意しないといけない」と感じたら、責務の分割を見直すきっかけになります。

テストピラミッドとその実践

「テストピラミッド」は、テストをどの粒度でどれだけ書くかの指針です。

       △ E2E(少数)
      ─────
     △△△ 統合テスト(中程度)
    ─────────
   △△△△△ 単体テスト(多数)
種類特徴書くべき量
E2Eテストユーザ操作を模倣。遅く不安定重要なハッピーパスのみ
統合テストDB・外部APIとの連携を確認主要なユースケース
単体テスト関数・メソッド単体を検証。高速で安定ロジックの多い部分を重点的に

現場でよく見る失敗は、ピラミッドが逆転しているケースです。E2Eテストを多数書いてしまい、実行が遅い・メンテが大変・フィードバックが遅いという問題が生まれます。

書くべきテスト・書かなくていいテスト

書く価値が高いテスト

ビジネスロジックを持つ部分

計算・条件分岐・変換処理など、間違えるとユーザへの影響が大きい部分です。

# 例:割引ロジック
def calculate_discount(price: int, user_rank: str) -> int:
    if user_rank == "premium":
        return int(price * 0.8)
    elif user_rank == "member":
        return int(price * 0.9)
    return price

このような関数は、境界値(price=0のとき、user_rankが想定外の値のとき)も含めてテストします。

バグ修正箇所

バグを直したら、必ずそのバグを再現するテストを書きます。「同じバグを二度起こさない」ためのドキュメントにもなります。

外部依存の境界部分

外部APIやDBとの接続部分は、モックを使ってテストします。外部サービスが落ちているときでもテストが通ることが重要です。

書く優先度を下げてよいテスト

  • 単純なゲッター・セッター:ロジックがなければ自明です
  • フレームワークの機能そのもの:フレームワークのテストはフレームワーク側で行われています
  • UIのピクセルレベルの確認:E2Eより手動確認が現実的なケースも多い

よくある失敗パターン

実装の詳細をテストしている

# ❌ 内部実装に依存したテスト
def test_calls_private_method():
    service._calculate_internal()  # プライベートメソッドを直接テスト

内部実装を変えるたびにテストが壊れ、リファクタリングの妨げになります。

# ✅ 外部から見た振る舞いをテスト
def test_returns_correct_total():
    result = service.get_order_total(order_id=1)
    assert result == 1500

テストが互いに依存している

テストは独立して実行できなければなりません。「このテストを先に実行しないと失敗する」という状況は、共有状態の管理が失敗しているサインです。

アサーションが一つもない

# ❌ 例外が出ないことだけを確認している
def test_process():
    service.process(data)  # 何も確認していない

これは「テストが存在している」ことの安心感だけを与え、実際には何も守っていません。

まとめ

価値のあるテストを書くために意識すべき3点です。

  1. 目的を明確にする——何を守るためのテストかを意識する
  2. ピラミッドのバランスを守る——E2Eより単体テストを充実させる
  3. 振る舞いをテストする——実装の詳細ではなく、外から見た動作を検証する

テストはコードです。設計が重要です。「とにかくカバレッジを上げる」という目標設定から離れ、「このテストがあることで何が守られるか」を問いながら書く習慣が、チームの品質を長期的に高めます。


関連記事としてコードレビューを120%活かす技術も参考になります。コードレビューとテスト設計は、品質を高める車の両輪です。

この記事はいかがでしたか?

記事一覧へ