tdd-workflow

Use when starting a new feature or function to write failing tests first, then implement the minimal code to pass (Red→Green→Refactor)

8 stars

Best use case

tdd-workflow is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Use when starting a new feature or function to write failing tests first, then implement the minimal code to pass (Red→Green→Refactor)

Teams using tdd-workflow should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/tdd-workflow/SKILL.md --create-dirs "https://raw.githubusercontent.com/drvoss/everything-copilot-cli/main/skills/development/tdd-workflow/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/tdd-workflow/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How tdd-workflow Compares

Feature / Agenttdd-workflowStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Use when starting a new feature or function to write failing tests first, then implement the minimal code to pass (Red→Green→Refactor)

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

Related Guides

SKILL.md Source

# TDD Workflow

## When to Use

- Building new features where correctness is critical
- Fixing bugs where a regression test should be written first
- Refactoring code that lacks test coverage
- Working on business logic, parsers, or data transformations

## Prerequisites

- Test framework installed and configured (Jest, pytest, Go test, etc.)
- Ability to run tests from the command line
- Understanding of the feature requirements or bug to reproduce

| Language | Common frameworks | Example commands |
|----------|-------------------|------------------|
| JavaScript / TypeScript | Jest, Vitest | `npm test`, `npx vitest` |
| Python | pytest | `pytest` |
| Go | go test | `go test ./...` |
| Ruby | RSpec, Minitest | `bundle exec rspec`, `ruby -Itest test/` |
| PHP | PHPUnit | `./vendor/bin/phpunit` |
| Rust | cargo test | `cargo test` |

> **tdd-guard users**: v1.6.8 adds Minitest reporter support and ignores ERB files by
> default. After `bundle add tdd-guard`, re-check its framework detection before relying
> on automatic enforcement.

## Workflow

### 1. Pre-Check — Does a Test Already Exist?

Before writing a new test, verify you are not duplicating existing coverage.

```text
glob pattern="**/*.{test,spec}.{js,jsx,ts,tsx}"
glob pattern="**/{test_*,*_test}.py"
glob pattern="**/*_test.go"
rg pattern="<function-or-behavior-name>" path="."
```

If an equivalent test already exists, extend or correct it instead of creating a
duplicate. If not, proceed to write the failing test.

### 2. Red — Write a Failing Test

Identify the behavior to implement, then write a test that asserts it.

```bash
# Find existing test files to follow conventions
```

```text
glob pattern="**/*.test.{ts,js,tsx}" # or **/*_test.go, **/test_*.py
```

Write a focused test using `create` or `edit`:

```javascript
// example: src/utils/parse.test.ts
describe('parseConfig', () => {
  it('should return default values when input is empty', () => {
    expect(parseConfig({})).toEqual({ timeout: 30, retries: 3 });
  });
});
```

Run the test and confirm it **fails**:

```powershell
npm test -- --testPathPattern="parse.test" 2>&1 | Select-Object -Last 20
```

### 3. Green — Write Minimal Code to Pass

Implement only enough code to make the failing test pass. Resist the urge to add extras.

```typescript
// src/utils/parse.ts
export function parseConfig(input: Record<string, unknown>) {
  return { timeout: input.timeout ?? 30, retries: input.retries ?? 3 };
}
```

Run the test again and confirm it **passes**:

```powershell
npm test -- --testPathPattern="parse.test"
```

### 4. Refactor — Clean Up Without Changing Behavior

Improve code structure, naming, and duplication while keeping all tests green.

**Exception — type-only edits**: Adding type annotations, cleaning up imports, or
renaming for clarity without changing behavior does not require a prior failing test.
Restrict this exception to structural changes only; any logic change must still go
through Red → Green first.

```powershell
# Run full test suite to ensure nothing else broke
npm test 2>&1 | Select-Object -Last 30
```

### 5. Repeat the Cycle

Add the next test case for edge cases or the next slice of behavior:

- Null/undefined inputs
- Boundary values
- Error conditions
- Integration with adjacent modules

### 6. Check Coverage

```powershell
npm test -- --coverage --collectCoverageFrom="src/utils/parse.ts"
```

Aim for **≥80% line coverage** on new code, **100% branch coverage** on critical paths.

## Examples

### Bug Fix TDD

```powershell
# 1. Reproduce the bug as a test
#    grep for the failing function, write a test with the exact input that causes the bug
grep -n "calculateTotal" src/billing/*.ts

# 2. Run and see it fail
npm test -- --testPathPattern="billing"

# 3. Fix the code
# 4. Run and see it pass
# 5. Add edge-case tests around the fix
```

### Multi-file Feature

```powershell
# Use the task agent to run tests continuously while you code
# Start test watcher in async mode
npx jest --watch --testPathPattern="feature" # mode: async
```

## Common Rationalizations

| Rationalization | Reality |
|----------------|---------|
| "This code is too simple to need tests" | Simple code still has edge cases. A 5-line function can have 3. |
| "I'll add tests later" | Later never comes. Untested code becomes legacy code overnight. |
| "It works, I can see it" | Manual verification is not reproducible. CI will catch what you missed. |
| "Copilot wrote it, so it must be right" | AI-generated code requires the same verification standards as human-written code. |
| "Tests still pass after refactoring" | Tests must exist before refactoring — otherwise they provide no safety net. |

## TDD Guardrail Enforcement

Prevent implementation code from being committed without a corresponding failing test.
Add this pre-commit check to your workflow:

```powershell
# Verify that implementation files have corresponding test files staged
$stagedSrc = git diff --cached --name-only | Where-Object { $_ -match "^src/" -and $_ -notmatch "\.test\." }
$stagedTests = git diff --cached --name-only | Where-Object { $_ -match "\.test\." }

if ($stagedSrc -and -not $stagedTests) {
    Write-Error "TDD violation: source files staged without corresponding test files."
    Write-Error "Write a failing test first, then implement."
    exit 1
}
```

To enforce this automatically, add it as a Git pre-commit hook:

```powershell
# .git/hooks/pre-commit (chmod +x on Unix)
# See rules/common/testing.md for the full hook content
```

## Red Flags

- Tests written after implementation with 100% pass rate (tests never saw a red state)
- Test files committed after source files
- Tests like `it('works')` with no meaningful assertions
- Tests that call internal implementations directly (testing private methods instead of public API)
- Tests written only to meet coverage numbers with no real assertions
- AI-generated implementation code committed before a failing test was written

## Verification

- [ ] Confirmed tests started in a Red state (via commit history or direct observation)
- [ ] `npm test` (or equivalent) exits with code 0
- [ ] Line coverage ≥80% and branch coverage ≥90% on critical paths for new code
- [ ] Each test runs independently (no ordering dependencies)
- [ ] Tests exist for edge cases (null, empty values, boundary values)

## Tips

- Write the **assertion first**, then work backward to the setup
- Each test should verify **one behavior** — keep tests small and descriptive
- Name tests as sentences: `it('rejects negative quantities')`
- Use `task` agent to run tests so output stays clean in your main context
- If you can't write a test easily, the code may need a better interface — that's valuable feedback
- Commit after each Green phase so you can safely revert a failed refactor
- Claude Code users can also study
  [tdd-guard](https://github.com/nizos/tdd-guard) for hook-based TDD enforcement

Related Skills

sprint-workflow

8
from drvoss/everything-copilot-cli

Use when starting a new feature, refactor, or multi-step dev task — runs the full sprint cycle (Think → Plan → Build → Review → Test → Ship → Monitor) using Copilot CLI's plan/autopilot modes.

github-pr-workflow

8
from drvoss/everything-copilot-cli

Use when creating, updating, or managing pull requests — automates the full PR lifecycle (open, review requests, labels, merge) via GitHub MCP

verification-before-completion

8
from drvoss/everything-copilot-cli

Use before claiming any task is done — run the exact command that proves the fix works, read the output, and only then report success.

using-git-worktrees

8
from drvoss/everything-copilot-cli

Use when you need multiple branches checked out at once — create isolated working directories for parallel development without cloning the repository repeatedly

triage

8
from drvoss/everything-copilot-cli

Use when a single issue needs structured triage — classify it, reproduce if needed, request missing information, and leave a durable brief or close-out note in the tracker.

to-issues

8
from drvoss/everything-copilot-cli

Use when a plan, spec, or PRD must become an actionable backlog — break it into thin dependency-aware issues that each deliver a verifiable vertical slice

sprint-retro

8
from drvoss/everything-copilot-cli

Use at the end of a sprint to run a data-driven retrospective — analyzes session history and git metrics to surface what shipped, what slowed you down, and concrete improvements.

security-audit

8
from drvoss/everything-copilot-cli

Use when a codebase needs a formal security audit beyond a quick scan — applies OWASP Top 10 and STRIDE threat modeling from a CSO perspective to surface systemic vulnerabilities.

release

8
from drvoss/everything-copilot-cli

Use when a sprint or feature is complete and ready to ship — tags the version, generates GitHub Release notes, runs rollout or smoke verification, and publishes to npm/PyPI/Docker registries.

prompt-optimizer

8
from drvoss/everything-copilot-cli

Use when a rough prompt, vague idea, or task description needs to become a finished copy-pasteable prompt for a chat-based LLM - rewrite it into one ready-to-send prompt with no blanks, no placeholders, and a clear output shape.

outside-voice

8
from drvoss/everything-copilot-cli

Use when you need an independent second opinion before, during, or after implementation — run challenge, consult, or review mode in a direct builder-to-builder voice

llm-wiki

8
from drvoss/everything-copilot-cli

Use when research or domain knowledge keeps getting rediscovered across sessions — build a supplementary markdown wiki that compounds synthesized knowledge without replacing GitHub or committed project guidance