Claude Code Plugins

Community-maintained marketplace

Feedback

Guide for implementing features using Test-Driven Development (TDD) methodology. Use when: (1) User requests to implement a feature using TDD, (2) User asks to write tests first before implementation, (3) User mentions Red-Green-Refactor cycle, (4) Starting a new feature that requires systematic testing. This skill provides step-by-step TDD workflows, concrete test patterns, and best practices for writing tests before implementation in TypeScript/React projects using Vitest.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name tdd
description Guide for implementing features using Test-Driven Development (TDD) methodology. Use when: (1) User requests to implement a feature using TDD, (2) User asks to write tests first before implementation, (3) User mentions Red-Green-Refactor cycle, (4) Starting a new feature that requires systematic testing. This skill provides step-by-step TDD workflows, concrete test patterns, and best practices for writing tests before implementation in TypeScript/React projects using Vitest.

TDD (Test-Driven Development)

Overview

This skill guides you through implementing features using the Test-Driven Development methodology. TDD ensures high code quality, comprehensive test coverage, and designs that emerge from tests.

Core TDD Cycle

๐Ÿ”ด Red โ†’ ๐ŸŸข Green โ†’ ๐Ÿ”ต Refactor
  โ†‘                           โ†“
  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

๐Ÿ”ด Red: Write a Failing Test

  1. Choose ONE feature to implement
  2. Write the simplest test for that feature
  3. Run the test and confirm it fails
  4. Verify the failure reason is correct

Example:

// Test for a feature that doesn't exist yet
describe('calculateTotal', () => {
  it('้…ๅˆ—ใฎ้‡‘้กใ‚’ๅˆ่จˆใ™ใ‚‹', () => {
    const result = calculateTotal([100, 200, 300])
    expect(result).toBe(600)
  })
})

// Run: โŒ FAIL - ReferenceError: calculateTotal is not defined

๐ŸŸข Green: Make It Pass (Minimal Implementation)

  1. Write the minimum code to make the test pass
  2. Don't add features not covered by tests
  3. Don't optimize prematurely

Example:

function calculateTotal(amounts: number[]): number {
  return amounts.reduce((sum, amount) => sum + amount, 0)
}

// Run: โœ… PASS

๐Ÿ”ต Refactor: Improve While Green

  1. Improve code quality without changing behavior
  2. Keep all tests passing
  3. Focus on: DRY, naming, structure, types

When to refactor:

  • After tests pass
  • Code duplication appears
  • Naming can be clearer
  • Function is too complex

Detailed guidance: See references/tdd_cycle.md


TDD Workflow

Step 1: Identify the Feature

Break down the feature into small, testable units:

Feature: Savings history display
โ†“
Sub-features:
- Display list of savings records
- Show records in descending order (newest first)
- Display empty state when no records
- Format dates correctly

Step 2: Write the First Test (Red)

Start with the simplest case:

import { describe, it, expect } from 'vitest'

describe('SavingsHistoryPage', () => {
  it('่ฒฏ้‡‘่จ˜้Œฒใฎใƒชใ‚นใƒˆใ‚’่กจ็คบใ™ใ‚‹', () => {
    // Arrange
    const records = [
      { id: '1', amount: 800, recordedAt: new Date('2025-01-01') }
    ]

    // Act
    render(<SavingsHistoryPage records={records} />)

    // Assert
    expect(screen.getByText('ยฅ800')).toBeInTheDocument()
  })
})

// Run: โŒ FAIL - SavingsHistoryPage is not defined

Step 3: Implement (Green)

export function SavingsHistoryPage({ records }: Props) {
  return (
    <div>
      {records.map(record => (
        <div key={record.id}>ยฅ{record.amount}</div>
      ))}
    </div>
  )
}

// Run: โœ… PASS

Step 4: Add Next Test (Red)

it('่จ˜้ŒฒใŒใชใ„ๅ ดๅˆใฏใ€Œใพใ ่จ˜้ŒฒใŒใ‚ใ‚Šใพใ›ใ‚“ใ€ใจ่กจ็คบใ™ใ‚‹', () => {
  render(<SavingsHistoryPage records={[]} />)

  expect(screen.getByText('ใพใ ่จ˜้ŒฒใŒใ‚ใ‚Šใพใ›ใ‚“')).toBeInTheDocument()
})

// Run: โŒ FAIL - Unable to find element

Step 5: Implement (Green)

export function SavingsHistoryPage({ records }: Props) {
  if (records.length === 0) {
    return <div>ใพใ ่จ˜้ŒฒใŒใ‚ใ‚Šใพใ›ใ‚“</div>
  }

  return (
    <div>
      {records.map(record => (
        <div key={record.id}>ยฅ{record.amount}</div>
      ))}
    </div>
  )
}

// Run: โœ… PASS

Step 6: Refactor (Blue)

export function SavingsHistoryPage({ records }: Props) {
  if (records.length === 0) {
    return <EmptyState />
  }

  return <RecordList records={records} />
}

// Extracted components for better structure
// Run: โœ… PASS (all tests still pass)

Best Practices

1. Small Steps

Good:

  • Write 1 test
  • Make it pass
  • Repeat

Avoid:

  • Writing multiple tests before implementing
  • Implementing features without tests

2. Test Naming (Japanese)

Use descriptive Japanese names that explain the specification:

describe('SavingsRecordRepository', () => {
  describe('ๆญฃๅธธ็ณป', () => {
    it('ๆญฃใฎ้‡‘้กใงใƒฌใ‚ณใƒผใƒ‰ใ‚’ไฝœๆˆใงใใ‚‹', () => {})
    it('้‡‘้กใŒ0ใฎๅ ดๅˆใ‚‚ไฝœๆˆใงใใ‚‹', () => {})
  })

  describe('็•ฐๅธธ็ณป', () => {
    it('่ฒ ใฎ้‡‘้กใฎๅ ดๅˆใฏใ‚จใƒฉใƒผใ‚’ๆŠ•ใ’ใ‚‹', () => {})
    it('NaNใฎๅ ดๅˆใฏใ‚จใƒฉใƒผใ‚’ๆŠ•ใ’ใ‚‹', () => {})
  })
})

3. AAA Pattern (Arrange-Act-Assert)

Structure every test with three sections:

it('ใƒฌใ‚ณใƒผใƒ‰ใ‚’ไฝœๆˆใ™ใ‚‹', async () => {
  // Arrange: Setup test data
  const repository = new SavingsRecordRepository()
  const input = { amount: 800 }

  // Act: Execute the function
  const result = await repository.create(input)

  // Assert: Verify expectations
  expect(result.amount).toBe(800)
})

4. One Assertion Concept Per Test

// โœ… Good: One concept per test
it('ไฝœๆˆใ—ใŸใƒฌใ‚ณใƒผใƒ‰ใฎ้‡‘้กใŒๆญฃใ—ใ„', () => {
  const result = repository.create({ amount: 800 })
  expect(result.amount).toBe(800)
})

it('ไฝœๆˆใ—ใŸใƒฌใ‚ณใƒผใƒ‰ใซๆ—ฅๆ™‚ใŒ่จ˜้Œฒใ•ใ‚Œใ‚‹', () => {
  const result = repository.create({ amount: 800 })
  expect(result.recordedAt).toBeInstanceOf(Date)
})

// โŒ Avoid: Multiple unrelated concepts
it('ใƒฌใ‚ณใƒผใƒ‰ใŒๆญฃใ—ใไฝœๆˆใ•ใ‚Œใ‚‹', () => {
  const result = repository.create({ amount: 800 })
  expect(result.amount).toBe(800)
  expect(result.recordedAt).toBeInstanceOf(Date)
  expect(result.id).toBeDefined()
})

Test Priority Order

Follow this order when writing tests:

  1. Happy Path: Most common use case
  2. Boundary Values: 0, empty, min, max
  3. Error Cases: Invalid input, exceptions
  4. Edge Cases: Special scenarios

Example sequence:

// 1. Happy Path
it('ๆญฃใฎ้‡‘้กใ‚’ๅˆ่จˆใ™ใ‚‹', () => {
  expect(calculateTotal([100, 200])).toBe(300)
})

// 2. Boundary
it('็ฉบ้…ๅˆ—ใฎๅ ดๅˆใฏ0ใ‚’่ฟ”ใ™', () => {
  expect(calculateTotal([])).toBe(0)
})

// 3. Error Case
it('NaNใ‚’ๅซใ‚€ๅ ดๅˆใฏใ‚จใƒฉใƒผใ‚’ๆŠ•ใ’ใ‚‹', () => {
  expect(() => calculateTotal([NaN])).toThrow()
})

Quick Commands

# Run tests in watch mode (for TDD)
npm run test -- --watch

# Run all tests once
npm run test

# Run tests with coverage
npm run test:coverage

# Run unit tests only
npm run test -- --run --project=unit

TDD Checklist

Before committing:

  • All tests pass (Green)
  • Tests cover the feature specification
  • Test names clearly describe behavior
  • No commented-out tests
  • Refactoring complete (if needed)
  • No implementation without tests

Advanced Patterns

For detailed test patterns and examples:

  • TDD Cycle Details: references/tdd_cycle.md

    • Detailed Red-Green-Refactor workflow
    • Timing and rhythm
    • Common pitfalls
  • Test Patterns: references/test_patterns.md

    • AAA pattern examples
    • Mock patterns
    • Async testing
    • Edge cases and boundary values
    • Data-driven tests
    • Anti-patterns to avoid

Notes

  • Focus on behavior, not implementation: Test what the code does, not how it does it
  • Tests are specification: Test names and assertions should clearly document expected behavior
  • Small cycles: 5-15 minutes per Red-Green-Refactor cycle
  • Project conventions: Follow commit message format in /.claude/CLAUDE.md