ホーム / Skills / 開発効率化 / テスト駆動開発(TDD)
開発効率化

テスト駆動開発(TDD)

Jesse VincentMITSkill の言語: 英語
ライセンス確認済みスクリプトなし中リスク
デモ実行記録 · claude-sonnet-5-5 · 2026年10月
Using the test-driven-development skill, implement a TypeScript function slugify(title: string) that lowercases the text, trims it, turns runs of spaces into single hyphens and removes characters that are not letters, digits or hyphens. You cannot run commands here, so show each step of the process and say what you would run and what output you expect at each point.
Skill: test-driven-development
Read: writing-good-tests.md

I can't run commands or create files here, so nothing below has been executed. The "expected output" blocks are predictions in vitest's format. I also haven't looked at the repo's package.json, so I'm assuming vitest and src/slugify.ts plus src/slugify.test.ts. With jest the commands and output would differ slightly.

Cycle 1: lowercasing

RED. The break this test catches is slugify returning the input unchanged. The expected value is a hand-written literal.

// src/slugify.test.ts
import { describe, expect, test } from 'vitest';
import { slugify } from './slugify';

describe('slugify', () => {
  test('lowercases the title', () => {
    expect(slugify('Hello')).toBe('hello');
  });
});

Verify RED. Run npx vitest run src/slugify.test.ts. I expect an error rather than a failure, because the module doesn't exist yet:

FAIL  src/slugify.test.ts
Error: Failed to resolve import "./slugify" from "src/slugify.test.ts"

The skill says an error doesn't count as a failure, so I'd add only a stub to get a real assertion failure:

// src/slugify.ts
export function slugify(title: string): string {
  return '';
}

Re-run the same command. Expected:

FAIL  src/slugify.test.ts > slugify > lowercases the title
AssertionError: expected '' to be 'hello'
Tests  1 failed (1)

The feature is missing and there are no typos, so this is the right failure.

GREEN. This is the minimal code:

export function slugify(title: string): string {
  return title.toLowerCase();
}

Re-run. Expected: Tests 1 passed (1).

Cycle 2: trimming

RED. The break caught is leading or trailing whitespace surviving.

  test('trims surrounding whitespace', () => {
    expect(slugify('  Hello  ')).toBe('hello');
  });

Verify RED. Run the same command. Expected:

FAIL  ... > trims surrounding whitespace
AssertionError: expected '  hello  ' to be 'hello'
Tests  1 failed | 1 passed (2)

GREEN.

  return title.toLowerCase().trim();

Verify GREEN. Expected: Tests 2 passed (2).

Cycle 3: runs of spaces become a single hyphen

RED. The break caught is spaces being left in place, or each space becoming its own hyphen. The input has a single space and a run of three, so a / /g replacement would fail it.

  test('turns each run of spaces into a single hyphen', () => {
    expect(slugify('hello world')).toBe('hello-world');
    expect(slugify('hello   big   world')).toBe('hello-big-world');
  });

Verify RED. Expected:

AssertionError: expected 'hello world' to be 'hello-world'
Tests  1 failed | 2 passed (3)

GREEN.

  return title.toLowerCase().trim().replace(/ +/g, '-');

Verify GREEN. Expected: Tests 3 passed (3).

Cycle 4: stripping disallowed characters

RED. The breaks caught are punctuation surviving, and an over-eager strip that removes existing hyphens. Using well-known in the input covers the second one.

  test('removes characters that are not letters, digits or hyphens', () => {
    expect(slugify('Hello, World!')).toBe('hello-world');
    expect(slugify('Well-Known Thing #1')).toBe('well-known-thing-1');
  });

Verify RED. Expected:

AssertionError: expected 'hello,-world!' to be 'hello-world'
Tests  1 failed | 3 passed (4)

GREEN. The strip runs after lowercasing, so uppercase letters aren't removed by mistake.

  return title
    .toLowerCase()
    .trim()
    .replace(/ +/g, '-')
    .replace(/[^a-z0-9-]/g, '');

Verify GREEN. Expected: Tests 4 passed (4).

Refactor

I'd split the pipeline into one named step per line with no behavior change. I don't think it's worth doing here, since the chain is already four short lines. Re-run to confirm 4 passed if you do.

Final verification

The skill requires the whole project suite, not just my file. Run npx vitest run (or npm test if the repo defines it). I expect all test files to pass with no warnings. Any failure in a pre-existing test should be reported by name, even if slugify didn't cause it.

Mutation check

Mutation Test that fails
Remove .toLowerCase() Cycle 1
Remove .trim() Cycle 2
/ +/g → / /g Cycle 3, the three-space input
Remove the strip step Cycle 4
Strip regex without - in the allowed set Cycle 4, well-known
Strip before lowercasing Cycle 4, H and W would be removed

Every realistic mutation is caught.

Decisions for you

I left these out because you didn't specify them, and each would need its own failing test first:

  • Non-ASCII letters: café currently becomes caf, because "letters" is implemented as a-z. Unicode support would use \p{L} with the u flag.
  • Adjacent hyphens: Rock & Roll becomes rock--roll, because the space-to-hyphen step runs before the strip. Collapsing repeated hyphens, or trimming leading and trailing ones, is a separate behavior.
  • Tabs and newlines: only the space character is treated as a separator, as specified.

Real run in an isolated folder with only this skill installed. Only the Skill and Read tools were enabled, so no command was run and no file was written. The example is fictional. The model could not run the tests, so it says what it would run and what output it expects at each step. The model opened one of the skill's reference files.

できること

機能の追加、バグ修正、リファクタリング、挙動の変更でテスト駆動開発を徹底させます。鉄則は、先に失敗するテストがなければ本番コードを書かない、です。手順は、1 つの挙動につき最小のテストを 1 つ書き、実行して正しい理由で失敗することを確認し、通る最も単純なコードを書き、再実行して(自分のファイルだけでなく)プロジェクト全体のテストが緑であることを確認し、テストを緑に保ったままリファクタリングします。良い例と悪い例、よくある言い訳の表(「単純すぎてテスト不要」「あとでテストする」)、危険信号のリスト、検証チェックリスト、バグ修正の例を含みます。もう 1 つのファイル writing-good-tests.md は、テストが捕まえるべき破壊を先に言葉にする、モックではなく実際の挙動を確かめる、テスト専用コードを本番クラスに入れない、ミューテーションチェック、といった、本当に役立つテストの書き方を扱います。

向いている場面

テストで本当に何かを証明したいあらゆる変更、再発しないバグ修正。

補足とリスク

中リスク:モデルにテストコマンドを実行させ、テストはプロジェクトのコードを実行します。最も厳しいルールは、テストより先に書いたコードは削除してやり直す(「削除は削除」)というものです。これはモデルが書いたばかりのコードが対象ですが、念のため先にコミットまたはバックアップし、許されている例外(使い捨てのプロトタイプ、生成コード、設定ファイル)を最初に伝えてください。プロジェクト全体のテストの実行を求めるため、遅いテストや破壊的なテストの副作用はご自身の責任になります。口調は厳格で断定的で、これは意図された設計です。1 回試用しました。モデルはコマンドを実行できず、何を実行するかを説明しました。