November 29, 2025 (4mo ago) — last updated December 22, 2025 (3mo ago)

TDD Red‑Green‑Refactor 実践ガイド

Red‑Green‑Refactor の TDD サイクルを実践的に解説。ワークフロー、コード例、導入方法で品質と保守性を向上させます。

← Back to blog
Cover Image for TDD Red‑Green‑Refactor 実践ガイド

Red‑Green‑Refactor の TDD サイクルを実践的に学び、ワークフロー、例、導入のコツを通じて品質と保守性を高めましょう。小さな検証可能なステップで設計を進めることで、リスクを下げて確実に価値を届けられます。

TDD Red‑Green‑Refactor 実践ガイド

要約: Red‑Green‑Refactor の TDD サイクルを実践的に解説します。ワークフロー、コード例、導入のコツを通して品質と保守性を高めます。

はじめに

Red‑Green‑Refactor(レッド‑グリーン‑リファクタ)によるテスト駆動開発(TDD)は、失敗するテストから始めて最小限の実装で合格させ、テストを保ったままコードを改善するシンプルで規律あるワークフローです。小さな検証可能なステップで進めることで設計の質が上がり、リスクを低減できます。

TDD は単なるテスト作成ではなく、設計を導く実践です。まず期待される振る舞いを書いてから実装するため、推測を減らし、意図的に前進できます。市場や組織によって導入状況は異なりますが、多くのチームがアジャイルやテスト自動化を通じて恩恵を受けています1

テスト駆動開発のリズム

手描きの図で Red-Green-Refactor サイクルによるテスト駆動開発 (TDD) ワークフローを示しています。

Red‑Green‑Refactor の3つのステップはそれぞれ明確な目的を持ち、作業を小さく保ちます。

3つのステージの要点

  • Red(失敗するテスト): 最小の有用な振る舞いを表すテストを書く。テストは失敗するのが正しい状態。
  • Green(合格させる): テストを合格させるための最小限のコードを書く。シンプルさを優先する。
  • Refactor(リファクタ): テストがある状態でコードの可読性や構造を改善する。

リファクタを省くと技術的負債が蓄積します。小さな改善を繰り返すことが、長期的な生産性を支えます。

フェーズをまとめる

フェーズ目的開発者の目標
Red要件を具体化して検証する失敗する小さなテストを1つ書く
Green最小限で要件を満たすテストを通すための最小限のコードを追加
Refactor内部品質を改善する重複を除去し意図を明確にする

このリズムはチームの予測可能性を高め、設計の品質に直接寄与します。

実際の TDD サイクル — TypeScript / React の例

この記事では、LikeButton コンポーネントを例に Red‑Green‑Refactor を示します。実例を通じて、TDD がどのように設計を導くかを確認してください。

Red フェーズ: 要件を定義する

最初の要件は「コンポーネントがクラッシュせずにレンダリングされ、“Like” と表示する」ことです。実装前にテストを書きます。

// LikeButton.test.tsx
import React from "react";
import { render, screen } from "@testing-library/react";
import LikeButton from "./LikeButton";

describe("LikeButton", () => {
  it("renders a button with the initial text “Like”", () => {
    render(<LikeButton />);
    const likeButton = screen.getByRole("button", { name: /like/i });
    expect(likeButton).toBeInTheDocument();
  });
});

テストを実行すると、コンポーネントがまだないため失敗します。これが Red フェーズです。

Green フェーズ: 最小限の実装で合格させる

テストを満たすための最小限のコンポーネントを書きます。

// LikeButton.tsx
import React from "react";

const LikeButton = () => {
  return <button>Like</button>;
};

export default LikeButton;

テストが通れば Green は成功です。

Refactor フェーズ: 型と構造を整える

テストが通っている状態でリファクタを行います。ここでは型を追加して将来的な拡張に備えます。

// LikeButton.tsx (refactored)
import React, { FC } from "react";

type LikeButtonProps = {};

const LikeButton: FC<LikeButtonProps> = () => {
  return <button>Like</button>;
};

export default LikeButton;

テストは引き続き緑のままです。安全に改善できるのが TDD の強みです。

イテレーション例: クリックで状態を変える

新しい要件: クリックするとテキストが “Liked” に変わり、二度とクリックできないように無効化する。まず失敗するテストを書きます。

// LikeButton.test.tsx
it("changes text to “Liked” and becomes disabled when clicked", () => {
  render(<LikeButton />);
  const likeButton = screen.getByRole("button", { name: /like/i });
  fireEvent.click(likeButton);
  expect(likeButton).toHaveTextContent("Liked");
  expect(likeButton).toBeDisabled();
});

最小限の振る舞いを実装します。

// LikeButton.tsx
import React, { FC, useState } from "react";

type LikeButtonProps = {};

const LikeButton: FC<LikeButtonProps> = () => {
  const [liked, setLiked] = useState(false);
  const handleClick = () => setLiked(true);
  return (
    <button onClick={handleClick} disabled={liked}>
      {liked ? "Liked" : "Like"}
    </button>
  );
};

export default LikeButton;

テストが再び緑になります。各サイクルは小さな要件を安全に追加します。

コード品質とビジネス価値

手描きの天秤の図。TDD なしのソフトウェア開発(バグ多数、重い)と TDD ありの開発(問題少ない、軽い)を比較しています。

TDD は品質向上を通じてビジネス価値をもたらします。欠陥が早期に検出されれば修正コストは下がり、サポート工数や顧客離脱の低下にも寄与します。実証研究や業界レポートは、TDD とテスト自動化がデバッグ労力の削減や品質改善と関連すると報告しています23

一貫したテストスイートは、オンボーディングを速め、シニアエンジニアへの依存を減らす実行可能なドキュメントにもなります。CI/CD と組み合わせれば、コミットごとに品質ゲートを通すことで信頼できるリリースパイプラインを構築できます。

よくある TDD の落とし穴と回避法

TDD の利点を損なう一般的なアンチパターンと対処法を簡潔に示します。

単体テストの体裁をした統合テスト

問題点: テストが多くの外部依存を動かしてしまうと、テストは遅く壊れやすくなります。

対処法: 単一ユニットを孤立してテストし、外部依存はモックやスタブで置き換えます。統合テストは別のスイートで扱いましょう。真のユニットテストはネットワークやファイルシステムに触れてはいけません4

実装をテストしてしまう

問題点: テストが内部構造に依存すると、リファクタでテストが壊れやすくなります。

対処法: 公開 API と観測可能な振る舞いをテストしてください。振る舞いテストはリファクタに強く、価値あるドキュメントになります。

リファクタを省略する

問題点: テストが通ったら次へ進み、ゴチャついた実装を放置するケース。

対処法: リファクタを必須のステップにし、小さな改善を積み重ねて技術的負債を防ぎます。

チームとレガシーコードへの統合

手描きの図で、開発者が特性化テストと CI/CD を使ってレガシーコードに取り組んでいる様子を示しています。

TDD の導入は文化的変化でもあります。Definition of Done にテストを含め、実践的な学習を促し、成功事例を共有してください。

チーム内での促進法

  • ペアプログラミングで経験者がサイクルを示す
  • モブプログラミングで知識を広げる
  • ランチ&ラーンで実コードの事例を共有する

小さく始めて、初期の成果を根拠に広めていきましょう。

特性化テストでレガシーを安定化させる

未テストのコードには特性化(characterization)テストを書いて現在の振る舞いを文書化します。その上で安全にリファクタや機能追加を進められます。

CI/CD で自動化する

コミットごとに CI でテストを実行して即時フィードバックを得ます。これによりマージ前に品質を担保できます。CI 設定のベストプラクティスは、並列実行・テストの分離・フレークテストの検出と隔離です。関連ガイド: TDD ベストプラクティスCI/CD による品質保証

よくある質問(FAQ)

TDD は他のテストを置き換えますか?

いいえ。TDD は主に単体テストを設計ツールとして使いますが、統合テストやエンドツーエンド(E2E)テストは引き続き必要です。

データベースや外部 API とどう付き合うべきですか?

ビジネスロジックはモックやスタブで孤立させ、統合テストで実際の外部連携を検証します。これによりユニットテストは高速で信頼性が高くなります。

単純な UI コンポーネントをテストする価値はありますか?

あります。ただし実装ではなく振る舞いをテストしてください。ユーザーが見る・行うことを検証することで価値が出ます。

速答 Q&A(要点3つ)

Q: TDD を始める最小の一歩は? A: 新機能または非クリティカルなバグで1つの失敗テストを書き、リファクタを必須にすることです。

Q: チームがすぐに効果を実感できますか? A: 小さな成功は通常数スプリント以内に見え、回帰バグの減少やデバッグ工数の低下で価値が明確になります2

Q: レガシーコードでの最初の対処は? A: 特性化テストで現状を文書化し、CI を使って段階的に安全に改修を進めます。

参考文献

この記事中の主張は、以下の研究・レポート・解説に基づいています。さらに学びたい場合はリンク先を参照してください。

1.
Digital.ai, “State of Agile Report,” Digital.ai, https://digital.ai/resource-center/state-of-agile-report
2.
Basili, Victor R., and others, “An Empirical Study on the Effects of Test-Driven Development,” https://link.springer.com/article/10.1007/s10664-015-9378-2
3.
Google Cloud, “State of DevOps Report,” https://cloud.google.com/devops/state-of-devops
4.
Martin Fowler, “TestDrivenDevelopment,” https://martinfowler.com/bliki/TestDrivenDevelopment.html
← Back to blog
🙋🏻‍♂️

AIがコードを書きます。
あなたがそれを長持ちさせます。

AI加速の時代において、クリーンコードは単なる良い実践ではありません—スケールするシステムと自らの重みで崩壊するコードベースの違いです。