第25章:仮実装(まず通す)🩹

🎯 今日のゴール
- 「とにかく最短でGreenにする」やり方(仮実装=Fake It)を身につけるよ🥰
- そして、仮のまま放置しない(ちゃんと一般化して卒業する)までできるようにするよ✅
💡 仮実装(Fake It)ってなに?
超ざっくり言うと👇
- まずは 定数で返す / ベタ書きで通す
- 次のテストで そのベタ書きが壊れるようにして
- そこで 必要になった分だけ一般化していく
Kent Beck の TDD でも「まず定数を返して、だんだん変数に置き換えていく」って説明されてるよ🧁 (スタニスワフのテックノート)
🩹 いつ使うのが上手い?
こんなときにめっちゃ効くよ💪✨
- 実装がちょっと面倒で、いったん前に進みたいとき🚶♀️
- APIの形(関数名・戻り値)を先に固定したいとき🧷
- 「正解のロジック」を書く前に、テストの意図が合ってるか確認したいとき🔍
- AIが“立派すぎる実装”を出してきがちなとき(まずは小さく!)🤖🧯
✅ ルール(これ守ると気持ちよく回る)
- Greenは最短距離(汚くてOK、ただし仮)🩹
- 2本目のテストは「仕様追加」じゃなくて、仮実装を壊すために入れる💣
- 仮実装が残ってたら、それは未返済の借金💸(次のリファクタで返す!)
- 仮実装を増やすほど、プロダクトコードに
if (x===100)が増えて地獄😇 → 早めに一般化!
🧪 ハンズオン:税込み合計をTDDで作る(仮実装→一般化)💰✨
作る関数:totalWithTax(subtotal: number): number
仕様:税率10%で、端数は切り捨て(Math.floor)にするよ🧾
① Red:最初のテストを書く(1ケースだけ)🟥
まずは「100円 → 110円」だけ決めるよ🙂
// tests/totalWithTax.test.ts
import { describe, it, expect } from "vitest";
import { totalWithTax } from "../src/totalWithTax";
describe("totalWithTax", () => {
it("100円なら110円になる", () => {
expect(totalWithTax(100), "税率10%の税込が欲しいよ").toBe(110);
});
});
※Vitest の expect は、失敗時に分かりやすくするため 第2引数にメッセージを付けられるよ📣(地味に助かる!) (Vitest)
② Green:仮実装で通す(定数でOK)🟩🩹
はい、ここが今日の主役✨ いったん 110を返すだけで通すよ!
// src/totalWithTax.ts
export function totalWithTax(subtotal: number): number {
return 110; // 🩹 仮実装!いまはこれでOK
}
この時点では「200円でも110円になっちゃう」けど、まだテストが要求してないから気にしない🙆♀️ (“必要な分だけ”がTDDのコアだよ🧠)
③ Red:仮実装を壊すテストを追加する💣🟥
次は「200円 → 220円」を追加して、110固定を爆破するよ💥
// tests/totalWithTax.test.ts
import { describe, it, expect } from "vitest";
import { totalWithTax } from "../src/totalWithTax";
describe("totalWithTax", () => {
it("100円なら110円になる", () => {
expect(totalWithTax(100), "税率10%の税込が欲しいよ").toBe(110);
});
it("200円なら220円になる(仮実装を壊す)", () => {
expect(totalWithTax(200)).toBe(220);
});
});
④ Green:最小の一般化をする🟩✨
ここでやっと、ちゃんと計算するよ✅
// src/totalWithTax.ts
export function totalWithTax(subtotal: number): number {
return Math.floor(subtotal * 1.1);
}
⑤ Refactor:読みやすくする(でもやりすぎない)🧹✨
「1.1」って何?になりがちだから、意図が読めるようにだけするよ🙂
// src/totalWithTax.ts
const TAX_RATE = 0.1;
export function totalWithTax(subtotal: number): number {
return Math.floor(subtotal * (1 + TAX_RATE));
}
⑥ 仕上げ:端数のテストを1本だけ足す(不安つぶし)🧷🧪
端数が怖いから、1個だけ確認して安心しよ💕
it("101円なら111円になる(端数は切り捨て)", () => {
expect(totalWithTax(101)).toBe(111); // 101*1.1=111.1 → floorで111
});
🤖 AIの使い方(この章で強いプロンプト)✨
1) 「仮実装で最短Green」案を出させる🩹
- 「最短でGreenにする仮実装を提案して」
- 「ただし、次のテストで壊れる前提で、後で一般化する方針も書いて」
2) 「仮が残ってないか監査」させる🔍
- 「この実装の中に、仮実装が永住しそうな匂いがある?(定数、条件分岐、マジックナンバー)」
- 「残ってたら、最小の一般化手順を1ステップで提案して」
3) 「次に足すテスト」を出させる💣
- 「今の仮実装を確実に壊す次のテストケースを3つ出して」
- 「そのうち、いちばん小さい爆破はどれ?」
😵💫 よくある事故(ここだけ注意!)🚧
- 仮実装がそのまま本番コードになる(あるある😇) → “壊すテスト”が足りない or リファクタしてない
if (x===100)が増殖して、条件地獄になる🌪️ → 2〜3本目あたりで一般化へ移行!- AIが「完成形」を出して、学びが飛ぶ🛫 → まずは仮実装→壊す→一般化、の順番を固定しよ🧷
✅ チェック(合格ライン)🎉
- 1本目で 仮実装(定数) を入れても罪悪感ゼロで進めた🩹
- 2本目で 仮実装を壊す 目的のテストを入れられた💣
- 一般化して、仮が消えた(永住してない)🏠❌
- テスト失敗時に原因が分かる(メッセージや命名が効いてる)📣 (Vitest)
次の第26章は「三角測量(2例目で一般化)📐」で、**“壊すための2例目”じゃなくて、“答えを見つけるための2例目”**の考え方に入っていくよ〜😊✨