首页 / 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.

它做什么

在功能开发、修 bug、重构和行为变更时强制执行测试驱动开发。铁律是:没有先失败的测试,就不许写生产代码。循环是:为一个行为写一个最小的测试,运行并确认它因为正确的原因失败,写出最简单能通过的代码,再次运行并确认整个项目的测试套件都是绿的(而不只是你那个文件),然后在测试保持绿色的前提下重构。其中有好坏示例、常见借口对照表(“太简单不用测”“我之后再补测试”)、危险信号清单、验证清单和一个修 bug 的示例。另一份文件 writing-good-tests.md 讲怎样写出真正能抓住问题的测试:先说出测试要抓的那个破坏、测真实行为而不是测 mock、不把仅用于测试的代码放进生产类,以及变异检查。

适合什么场景

任何你想让测试真正能证明点什么的改动,以及修了就不再复发的 bug 修复。

说明与风险

中风险:它会让模型运行你的测试命令,而测试会执行你项目的代码。它最严格的规则是:先于测试写出的代码要删除并重新开始(“删除就是删除”);这针对的是模型刚写的代码,但为稳妥起见,请先提交或备份你的工作,并提前告诉它允许的例外(一次性原型、生成代码、配置文件)。它要求运行整个项目的测试套件,所以速度慢或有破坏性的测试带来的副作用要由你承担。语气严格而绝对,这是设计如此。已试用一次,模型无法运行命令,只说明了它会运行什么。