第30章:テスト側リファクタ(読み物として整える)🧹

🎯 目的
- テストを「仕様書として読める文章」にする📖💕
- 重複を減らしつつ、意図はむしろハッキリさせる🧠✨
- 失敗したときに“どの約束が破れたか”が一瞬で分かるようにする💥👀
まず大事な前提(超重要)🚦
- リファクタは “Greenのあと” ✅ つまり「テストも実装も全部通ってる状態」でやるやつだよ😊
- テスト側リファクタでやるのは 「意味を変えない」整形だけ🧼 (“仕様を変える”のは次の章や次のサイクルで✨)
今日の合言葉:DRYよりDAMP 🧴🌱
- アプリのコード:重複は悪になりがち(DRY)
- テストコード:説明力が正義(DAMP=Descriptive And Meaningful Phrases) → 同じ言葉を繰り返すことが、仕様の強調になることもあるよ🫶
なので第30章はこれ👇 ✅ “共通化しすぎない” ✅ 読めることを優先 ✅ でもコピペ地獄は卒業 🎓✨
よくある「読みにくいテスト」あるある😵💫
- テスト名が
test1とかshould workとか…何が仕様?😇 - Arrangeが長すぎて、何を試したいのか行方不明🫥
beforeEachに隠しすぎて、条件が見えない🕵️♀️- 期待値が雑で、落ちたときのヒントゼロ🥲
🧪 ハンズオン:汚テストを「読み物」にする(Before → After)✨
題材:calcTotalYen()(税込み・値引き・下限0の計算をする関数)
※中身はもう出来ててテストも通ってる想定でOKだよ😊
① Before:動くけど読めないテスト😇(ありがち)
import { describe, it, expect } from "vitest";
import { calcTotalYen } from "../src/calcTotalYen";
describe("calcTotalYen", () => {
it("test1", () => {
const result = calcTotalYen({
baseYen: 1000,
taxRate: 0.1,
discountYen: 0,
});
expect(result).toBe(1100);
});
it("test2", () => {
const result = calcTotalYen({
baseYen: 1000,
taxRate: 0.1,
discountYen: 100,
});
expect(result).toBe(1000);
});
it("test3", () => {
const result = calcTotalYen({
baseYen: 1000,
taxRate: 0.1,
discountYen: 2000,
});
expect(result).toBe(0);
});
});
問題点👀
test1/test2/test3が 仕様を語ってない- 数字が並んでるだけで ルールが見えない
- ケースが増えたらコピペ祭り確定🎆
② After:仕様が読めるテスト📘✨(おすすめ形)
ポイントはこの3つ💡
- テスト名を“約束”で書く
- 差分だけが見えるArrangeにする
- it.each(パラメータ化)で“同じ仕様の繰り返し”を整理
Vitestは test.each / it.each が使えるよ🧪(同じテストをデータ違いで回せるやつ) (Vitest)
import { describe, it, expect } from "vitest";
import { calcTotalYen } from "../src/calcTotalYen";
type Args = Parameters<typeof calcTotalYen>[0];
// ✅ “差分だけ見える”土台(共通化しすぎないのがコツ)
const baseArgs = (over: Partial<Args> = {}): Args => ({
baseYen: 1000,
taxRate: 0.1,
discountYen: 0,
...over,
});
describe("calcTotalYen", () => {
describe("税込み計算", () => {
it("base=1000, tax=10% のとき 1100円になる", () => {
const result = calcTotalYen(baseArgs());
// ✅ Vitestは expect の第2引数に “失敗時メッセージ” を入れられるよ✨
expect(result, "税込み(1000 * 1.1) は 1100 のはず").toBe(1100);
});
});
describe("値引き", () => {
it.each([
{ discountYen: 0, expected: 1100, label: "値引きなし" },
{ discountYen: 100, expected: 1000, label: "100円引き" },
])("$label のとき expected=$expected", ({ discountYen, expected }) => {
const result = calcTotalYen(baseArgs({ discountYen }));
expect(result, `discountYen=${discountYen} の結果`).toBe(expected);
});
});
describe("下限0(マイナスにならない)", () => {
it("値引きが大きすぎても 0円で止まる", () => {
const result = calcTotalYen(baseArgs({ discountYen: 2000 }));
expect(result, "合計がマイナスなら 0 に丸める").toBe(0);
});
});
});
✅ 改善されたこと🎉
- describe が 章立てになって、仕様が目に入る📚
baseArgs({ discountYen: 100 })みたいに 差分が1行でわかる✨it.eachで同系統のケースが 読みやすく増やせる🧪 (Vitest)expect(actual, "メッセージ")で、落ちたときのヒントが強い💪(Vitestの仕様だよ) (Vitest)
🧠 テスト側リファクタの「安全な手順」🛡️✨
おすすめの順番はこれ👇(事故りにくいよ😊)
-
テスト名を直す📝
何をしたら / どうなるを文章にする
-
AAAに整える🧱
- Arrange(準備)
- Act(実行)
- Assert(確認)
-
“重複の種類”を見分ける👀
- データの重複 →
baseArgs()みたいな薄いヘルパーでOK - 手順の重複 → まずは放置でもOK(隠すと読みにくくなること多い)
- データの重複 →
-
パラメータ化できるところだけ it.each🔁
- “同じルールの入力違い” だけね!(違うルールまで混ぜない)
-
失敗時メッセージを足す💬✨
- 「何の約束が破れた?」が出ると神👼
-
必要なら カスタムMatcher(やりすぎ注意!)🎭
- Vitestは
expect.extendで拡張できるよ (Vitest)
- Vitestは
🙅♀️ 共通化しすぎチェック(ここ超大事)🚨
次の匂いが出たら、共通化をやめるサイン💡
- ヘルパーの中でさらにヘルパー呼んでて 追跡がだるい🌀
beforeEachが長くて テスト本文がスカスカ🍃- “そのテストに必要な条件” が本文から見えない🫥
- 失敗ログを見ても「え、何が起きたの?」ってなる😇
テストって 読者(未来の自分&仲間) がいる文章だからね📘💕
🤖 AIの使い方(第30章向けテンプレ)✨
AIはめっちゃ相性いい章だよ😍(ただし判断は自分!)
1) 命名だけ助けてもらう📝
- 「このテストの意図を日本語1文にして」
- 「describe/itの名前案を5つ出して。誤解が少ない順に並べて」
2) 共通化しすぎ警察👮♀️
- 「このリファクタ案、意図が隠れてない?隠れてるなら指摘して」
- 「beforeEachに寄せた場合のデメリットを列挙して」
3) it.each 化の仕分け🧪
- 「同じルールとして it.each にまとめていいケースだけ選んで。混ぜたら危険なものも教えて」
✅ チェックリスト(合格ライン)💮✨
- テスト名だけ見て仕様が想像できる🙂
- Arrangeは“差分だけ”が目に入る👀
-
it.eachは「同じルール」の範囲で使えてる🔁 (Vitest) - 落ちたときに「何の約束が破れたか」分かる(メッセージ or 命名)💥 (Vitest)
- 共通化しすぎて本文がスカスカになってない🌿
🧾 第30章の“提出物”イメージ(3コミットでOK)📦✨
- ✅ commit1:
test: テスト名を仕様表現にする - ✅ commit2:
refactor(test): AAA整形 + baseArgs導入 - ✅ commit3:
refactor(test): it.eachで同ルールを整理(Vitest)
🔥 おまけ:最新ツール周りの注意(軽くでOK)
- Vitestは 4.0 が出ていて、メジャーアップデート時は migration をチラ見すると安心だよ🧭 (Vitest)
- NodeはLTS系が継続更新されていて、セキュリティリリースも出るから、たまにアップデートしてね🔒✨ (Node.js)
- TypeScriptも5.9系のリリースノートが公開されてるよ📌 (TypeScript)
次、もしよければ😊✨ あなたの「今いちばん読みにくいテスト」を(そのまま貼って)くれたら、**第30章のルールで“読み物化”**を一緒にやるよ🧹📘💕