テスト戦略

プロジェクトのテスト種類、範囲、自動化レベルを体系的に定義する計画

テスト品質
テスト戦略」の技術書を見る →

テスト戦略とは

テスト戦略は、プロジェクトで実施するテストの種類、範囲、自動化レベル、実行タイミングを体系的に定義する計画である。「何をどのレベルでテストするか」を明確にし、品質とスピードのバランスを取る。

テストピラミッド

テストピラミッドは Mike Cohn が 2009 年の著書『Succeeding with Agile』で「テスト自動化ピラミッド」として示したモデルで、Martin Fowler の解説を通じて広く知られるようになった。下層ほど対象が狭く高速・安定・低コストで、上層ほど対象が広く低速・不安定・高コストになる。この非対称性があるため、同じバグを捕まえられるなら下層で捕まえるほうが得になる。

        /  E2E テスト  \        ← 少数、遅い、高コスト
       / 結合テスト      \      ← 中程度
      / 単体テスト          \   ← 多数、速い、低コスト
レベル対象速度安定性本数の目安
単体テスト関数、クラスミリ秒高い70%
結合テストモジュール間の連携中程度20%
E2E テストユーザーフロー全体低い10%

この 70/20/10 は Google Testing Blog の 2015 年 4 月の記事が「最初の当たりを付けるための目安」として挙げた分割で、普遍的な正解ではない。同記事も適切な比率はチームごとに異なると述べている。守るべきなのは数字そのものではなく、上に行くほど本数が減るピラミッド型を保つという形である。逆に E2E テストや手動テストが最も多い状態はアイスクリームコーンと呼ばれるアンチパターンになる。

各テストレベルの役割

単体テスト

// ビジネスロジックの正確性を検証
describe('calculateDiscount', () => {
  it('10,000円以上で10%割引', () => {
    expect(calculateDiscount(15000)).toBe(1500);
  });
  it('10,000円未満は割引なし', () => {
    expect(calculateDiscount(5000)).toBe(0);
  });
});

結合テスト

// Lambda ハンドラー + DynamoDB の連携を検証 (createOrder / getOrder は別ハンドラー)
it('注文を作成して取得できる', async () => {
  const created = await createOrder({ body: JSON.stringify({ item: 'Book', qty: 1 }) });
  expect(created.statusCode).toBe(201);

  const orderId = JSON.parse(created.body).id;
  const fetched = await getOrder({ pathParameters: { id: orderId } });
  expect(fetched.statusCode).toBe(200);
  expect(JSON.parse(fetched.body).item).toBe('Book');
});

E2E テスト

// Playwright でユーザーフロー全体を検証
test('商品を購入できる', async ({ page }) => {
  await page.goto('/products');
  await page.click('[data-testid="add-to-cart"]');
  await page.click('[data-testid="checkout"]');
  await expect(page.locator('.order-confirmation')).toBeVisible();
});

CI での実行戦略

テストレベルごとに実行時間が桁で違うため、同じジョブにまとめると速いテストの結果も遅いテストを待つことになる。レベルごとにジョブを分けて並列に走らせ、遅い E2E テストだけ実行の契機を絞ると、開発者が待つ時間を短く保てる。

# .github/workflows/test.yml (アクションのメジャー版は 2026 年 8 月時点)
on:
  pull_request:
  push:
    branches: [main]

jobs:
  unit-test:                    # 全 PR で実行 (数秒)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 22
      - run: npm ci
      - run: npx vitest run

  integration-test:             # 全 PR で実行 (数分)
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 22
      - run: npm ci
      - run: npx vitest run --config vitest.integration.config.ts

  e2e-test:                     # main への push だけで実行 (数分〜数十分)
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 22
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test
  • 単体テスト: 全 PR で実行。失敗したらマージをブロック
  • 結合テスト: 全 PR で実行。外部依存はモックまたはローカル DB
  • E2E テスト: main マージ時またはデプロイ前に実行

テスト戦略の判断基準

どのレベルを厚くするかは、プロダクトの性質で変わる。同じ対象を確かめる方法が複数あるとき、下の表の左右を読み比べて「そのレベルで確かめられることと確かめられないこと」を突き合わせると選びやすい。

判断基準単体テストを増やすE2E テストを増やす
ビジネスロジックが複雑分岐と境界値を関数単位で網羅する経路が増えるだけで、条件の網羅には向かない
UI のインタラクションが重要描画された後の操作までは確かめられない実際の操作順で確かめる。主要な導線に絞る
外部サービスとの連携が多い通信部分を差し替えた結合テストで補う本物に繋ぐ経路を 1 本だけ通しておく
デプロイ頻度が高い数秒で結果が返り、1 日に何度でも回せる実行が長く、本数を増やすと出荷の足を引っ張る

現場での応用を知るには関連書籍も役立つ。

この記事は役に立ちましたか?

関連用語

関連する記事