Claude-initial-setup tdd-workflow

install
source · Clone the upstream repo
git clone https://github.com/VersoXBT/claude-initial-setup
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/VersoXBT/claude-initial-setup "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/testing/tdd-workflow" ~/.claude/skills/versoxbt-claude-initial-setup-tdd-workflow && rm -rf "$T"
manifest: skills/testing/tdd-workflow/SKILL.md
source content

TDD Workflow

Test-Driven Development means writing a failing test before writing implementation code. This ensures every feature has coverage and drives cleaner design through the red-green-refactor cycle.

When to Use

  • User is implementing a new feature (push for test-first)
  • User is fixing a bug (write a test that reproduces the bug first)
  • User asks about TDD or test-driven development
  • User asks how to structure tests
  • Coverage is below 80% and needs improvement

Core Patterns

The Red-Green-Refactor Cycle

  RED: Write a failing test
   |
   v
  GREEN: Write minimal code to pass
   |
   v
  REFACTOR: Clean up without changing behavior
   |
   v
  (repeat)

Step 1: RED — Write a Failing Test

Write the test first. It must fail because the implementation does not exist yet:

// src/utils/slug.test.ts
import { describe, it, expect } from 'vitest'
import { createSlug } from './slug'

describe('createSlug', () => {
  it('converts a title to a URL-safe slug', () => {
    expect(createSlug('Hello World')).toBe('hello-world')
  })

  it('removes special characters', () => {
    expect(createSlug('Hello & World!')).toBe('hello-world')
  })

  it('collapses multiple hyphens', () => {
    expect(createSlug('Hello   World')).toBe('hello-world')
  })

  it('trims leading and trailing hyphens', () => {
    expect(createSlug(' Hello World ')).toBe('hello-world')
  })
})

Run the test — it must FAIL:

npx vitest run src/utils/slug.test.ts
# FAIL: Cannot find module './slug'

Step 2: GREEN — Minimal Implementation

Write the minimum code to make all tests pass:

// src/utils/slug.ts
export function createSlug(title: string): string {
  return title
    .toLowerCase()
    .replace(/[^a-z0-9\s-]/g, '')
    .replace(/\s+/g, '-')
    .replace(/-+/g, '-')
    .replace(/^-|-$/g, '')
}

Run the test — it must PASS:

npx vitest run src/utils/slug.test.ts
# PASS: All 4 tests passed

Step 3: REFACTOR — Improve Without Changing Behavior

Clean up the implementation while keeping all tests green:

// src/utils/slug.ts (refactored)
const SPECIAL_CHARS = /[^a-z0-9\s-]/g
const WHITESPACE = /\s+/g
const MULTIPLE_HYPHENS = /-+/g
const EDGE_HYPHENS = /^-|-$/g

export function createSlug(title: string): string {
  return title
    .toLowerCase()
    .replace(SPECIAL_CHARS, '')
    .replace(WHITESPACE, '-')
    .replace(MULTIPLE_HYPHENS, '-')
    .replace(EDGE_HYPHENS, '')
}

Run tests again to confirm nothing broke:

npx vitest run src/utils/slug.test.ts
# PASS: All 4 tests passed

Bug Fix TDD

For bug fixes, write a test that reproduces the bug first:

// 1. Bug report: createSlug crashes on empty string

// 2. RED: Write test that demonstrates the bug
it('handles empty string', () => {
  expect(createSlug('')).toBe('')
})

// 3. Run test — confirms the bug exists (FAIL or unexpected behavior)

// 4. GREEN: Fix the implementation
export function createSlug(title: string): string {
  if (!title) return ''
  // ... rest of implementation
}

// 5. Run test — bug is fixed (PASS)

Test Pyramid

Structure tests by scope and speed:

        /  E2E  \          Few, slow, high-value
       /----------\        (Playwright, Cypress)
      / Integration \      Moderate count, medium speed
     /----------------\    (API tests, DB tests)
    /    Unit Tests     \  Many, fast, focused
   /----------------------\ (Vitest, Jest, pytest)

Coverage targets by layer:

LayerTargetSpeedCount
Unit80%+ line coverage< 1s eachMany
IntegrationKey workflows< 5s eachModerate
E2ECritical paths< 30s eachFew

Coverage Verification

Check coverage after each TDD cycle:

# Vitest
npx vitest run --coverage

# Jest
npx jest --coverage

# pytest
python -m pytest --cov=src --cov-report=term-missing

Target 80% minimum overall. Focus on:

  • All public functions/methods
  • All error paths
  • All edge cases (empty input, null, boundary values)

TDD for API Endpoints

// 1. RED: Write the integration test
describe('POST /api/users', () => {
  it('creates a user and returns 201', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({ email: 'test@example.com', name: 'Test' })

    expect(response.status).toBe(201)
    expect(response.body.data.email).toBe('test@example.com')
  })

  it('returns 400 for invalid email', async () => {
    const response = await request(app)
      .post('/api/users')
      .send({ email: 'not-an-email', name: 'Test' })

    expect(response.status).toBe(400)
    expect(response.body.error).toContain('email')
  })
})

// 2. GREEN: Implement route, validation, and handler
// 3. REFACTOR: Extract validation schema, clean up handler

Anti-Patterns

  • Writing tests after implementation: This is "test-after" not TDD. Tests written after code tend to test the implementation rather than the behavior, and they always pass on first run — so you never see RED.
  • Skipping the RED step: If your test passes on the first run, either the feature already exists or the test is wrong. A passing new test is suspicious.
  • Over-implementing in GREEN: Write the minimum to pass, not the "best" solution. Optimization comes in REFACTOR. Minimal GREEN keeps you honest about what the tests actually require.
  • Skipping REFACTOR: Going RED-GREEN-RED-GREEN without refactoring accumulates technical debt. Always clean up after GREEN.
  • Testing implementation details: Test behavior (what), not implementation (how).
    expect(result).toBe('hello-world')
    is good.
    expect(internalRegex).toHaveBeenCalled()
    is brittle.
  • Fixing tests to match broken code: If a test fails after a code change, the code is probably wrong, not the test. Fix the implementation first.

Quick Reference

1. RED:      Write test -> Run -> Must FAIL
2. GREEN:    Write minimal code -> Run -> Must PASS
3. REFACTOR: Clean up -> Run -> Must still PASS
4. COVERAGE: Check -> Must be 80%+
5. REPEAT

Bug fixes: Always reproduce with a test first. New features: Always write the test before the implementation. Coverage: 80% minimum. Check with

--coverage
flag. Pyramid: Many unit tests, moderate integration, few E2E.