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)
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
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/tdd-workflow/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How tdd-workflow Compares
| Feature / Agent | tdd-workflow | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/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
Best AI Skills for Claude
Explore the best AI skills for Claude and Claude Code across coding, research, workflow automation, documentation, and agent operations.
Cursor vs Codex for AI Workflows
Compare Cursor and Codex for AI coding workflows, repository assistance, debugging, refactoring, and reusable developer skills.
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 enforcementRelated Skills
sprint-workflow
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
Use when creating, updating, or managing pull requests — automates the full PR lifecycle (open, review requests, labels, merge) via GitHub MCP
verification-before-completion
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
Use when you need multiple branches checked out at once — create isolated working directories for parallel development without cloning the repository repeatedly
triage
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
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
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
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
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
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
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
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