홈 / 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.

하는 일

기능 개발, 버그 수정, 리팩터링, 동작 변경에서 테스트 주도 개발을 강제합니다. 철칙은 먼저 실패하는 테스트가 없으면 프로덕션 코드를 쓰지 않는다입니다. 순환은 이렇습니다. 하나의 동작에 대해 최소한의 테스트 하나를 쓰고, 실행해 올바른 이유로 실패하는지 확인하고, 통과하는 가장 단순한 코드를 쓰고, 다시 실행해 (내 파일만이 아니라) 프로젝트 전체 테스트가 녹색인지 확인한 뒤, 테스트를 녹색으로 유지하며 리팩터링합니다. 좋은 예와 나쁜 예, 흔한 핑계 표("너무 단순해서 테스트 불필요", "나중에 테스트하겠다"), 위험 신호 목록, 검증 체크리스트, 버그 수정 예시가 있습니다. 또 다른 파일 writing-good-tests.md는 테스트가 잡아야 할 깨짐을 먼저 말하기, 목이 아닌 실제 동작 검증하기, 테스트 전용 코드를 프로덕션 클래스에 넣지 않기, 뮤테이션 점검 등 정직한 테스트를 쓰는 법을 다룹니다.

이런 때 좋습니다

테스트로 실제로 무언가를 증명하고 싶은 모든 변경, 다시 깨지지 않는 버그 수정.

참고 및 위험

중간 위험:모델이 테스트 명령을 실행하게 하며, 테스트는 프로젝트 코드를 실행합니다. 가장 엄격한 규칙은 테스트보다 먼저 쓴 코드는 지우고 다시 시작하라("삭제는 삭제")는 것으로, 모델이 방금 쓴 코드를 겨냥하지만 안전을 위해 먼저 커밋하거나 백업하고, 허용되는 예외(일회용 프로토타입, 생성된 코드, 설정 파일)를 미리 알려 주세요. 프로젝트 전체 테스트 실행을 요구하므로 느리거나 파괴적인 테스트의 부작용은 사용자 몫입니다. 어조는 설계상 엄격하고 단정적입니다. 한 번 시험 실행했고, 모델은 명령을 실행할 수 없어 무엇을 실행할지 설명했습니다.