| name | add-test-writer |
| description | [ADD v0.11.0] Write failing tests from spec (TDD RED phase) |
| argument-hint | specs/{feature}.md [--ac AC-001,AC-002] [--type unit|integration|e2e] |
ADD Test Writer Skill v0.11.0
Generate comprehensive failing tests from a feature specification. This is the RED phase of TDD — write tests before implementation.
Overview
Test Writer converts acceptance criteria and user test cases from a spec into failing, runnable tests. The output is test files that:
- Compile and execute but fail (RED state)
- Have clear, descriptive names mapped to ACs
- Cover all acceptance criteria and user test cases
- Follow the project's test conventions
- Are production-quality (good error messages, clear assertions)
Pre-Flight Checks
-
Verify spec file exists and is readable
- Read the spec at the provided path
- Extract frontmatter: feature name, version, status
-
Parse acceptance criteria
- Read the Acceptance Criteria section
- Extract each AC with its ID (AC-001, AC-002, etc.)
- Extract the criteria description and conditions
-
Parse user test cases
- Read the User Test Cases section (if present)
- Extract each UT with ID (UT-001, UT-002, etc.)
- Note the setup, actions, and expected outcomes
-
Load test framework configuration
- Read .add/config.json
- Identify test.framework (jest, pytest, vitest, mocha, pytest, etc.)
- Load test.convention (naming pattern, directory structure, imports)
- Check test.type to determine default test type
-
Identify test file location
- Determine target directory from config (typically
tests/ or __tests__/)
- Determine file naming from convention
- Check if test file already exists; if so, append new tests
-
Check for session handoff — per the Session-Handoff Preflight in ~/.codex/add/references/skill-epilogue.md
Execution Steps
Step 1: Analyze Acceptance Criteria
For each acceptance criterion:
- Extract the requirement statement
- Break down into testable assertions
- Identify input conditions, actions, and expected outputs
- Note any data setup required
- Consider both happy path and edge cases
Example AC:
AC-001: When user clicks the "Submit" button with valid form data,
the form should POST to /api/submit and display a success message.
Maps to tests:
test_AC_001_submit_valid_data_posts_to_api
test_AC_001_submit_valid_data_displays_success_message
test_AC_001_submit_shows_error_on_network_failure (edge case)
Step 2: Design Test Structure
For each AC, determine:
- Test type from --type flag or config default
unit: Test individual functions/components in isolation
integration: Test feature interactions with other modules
e2e: Test complete user workflows end-to-end
- Test scope: What needs to be mocked vs. real
- Setup requirements: Fixtures, test data, mocks
- Assertions: What the test verifies
Step 3: Generate Test Files
Create test file(s) following the framework conventions:
For Jest/Vitest (JavaScript/TypeScript):
- File:
tests/{feature}.test.ts or __tests__/{feature}.test.js
- Import testing libraries and mocks
- Use
describe() blocks for AC grouping
- Use
test() or it() for individual test cases
- Include setup/teardown with
beforeEach(), afterEach()
Example template:
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
describe('Feature Name', () => {
describe('AC-001: requirement statement', () => {
beforeEach(() => {
});
it('should test specific behavior', () => {
const input = { };
const result = functionUnderTest(input);
expect(result).toBe(expectedValue);
});
});
});
For pytest (Python):
- File:
tests/test_{feature}.py
- Use
class Test{Feature}: for grouping by AC
- Use
def test_ac_NNN_description(self): for test methods
- Use
pytest.fixture for setup
- Use
assert statements
Example template:
import pytest
class TestFeatureName:
"""Tests for Feature Name"""
@pytest.fixture
def setup(self):
yield
class TestAC001:
"""AC-001: requirement statement"""
def test_specific_behavior(self, setup):
input_data = { /* test data */ }
result = function_under_test(input_data)
assert result == expected_value
Step 4: Ensure Tests Fail
After generating tests:
-
Attempt to compile/run tests
npm test
python -m pytest
-
Verify they fail with clear messages
- Every test must fail (exit code non-zero)
- Error messages should indicate missing implementation
- Examples:
- "Cannot find module: './feature'" (module doesn't exist)
- "NameError: name 'feature_function' is not defined"
- "AssertionError: undefined !== 'expected value'"
-
If tests don't fail
- Implementation may already exist (unexpected)
- Or test is incorrectly written (syntax error)
- Fix test syntax errors; if implementation exists, ask user to clarify
Step 4b: Capture RED snapshot (v0.9.0 — test-deletion guardrail)
Before returning control to the tdd-cycle orchestrator, capture the test surface
snapshot so Gate 3.5 can later detect deletions:
python3 ~/.codex/add/../../scripts/check-test-count.py snapshot \
--phase red \
--cycle-id {N} \
--spec-slug {slug} \
--base-sha {cycle-base-sha} \
--fail-on-empty
Requirements (AC-005, AC-006):
- The snapshot file MUST be committed before exiting RED
- Commit message:
test(red): snapshot {total_functions} tests for {slug}
- If
--fail-on-empty reports zero tests, halt with the structured error
"RED phase produced no failing tests — TDD violation."
The snapshot lives at .add/cycles/cycle-{N}/tdd-{slug}-red.json and captures: test
files, function names, normalized body hashes, language, git base SHA, and phase-end
SHA. /add-verify Gate 3.5 reads this file alongside the GREEN snapshot to enforce
the test-deletion invariant. See core/rules/tdd-enforcement.md.
Step 5: Document Test Mapping
Create a test mapping file: tests/{feature}-mapping.md
# Test Mapping for {Feature Name}
## Acceptance Criteria Coverage
| AC ID | Description | Test File | Test Function | Status |
|-------|-------------|-----------|-----------------|--------|
| AC-001 | requirement | tests/{feature}.test.ts | test_AC_001_* | ✗ FAIL |
| AC-002 | requirement | tests/{feature}.test.ts | test_AC_002_* | ✗ FAIL |
## User Test Cases Coverage
| UT ID | Description | Test File | Test Function | Status |
|-------|-------------|-----------|-----------------|--------|
| UT-001 | scenario | tests/{feature}.test.ts | test_UT_001_* | ✗ FAIL |
## Notes
- All tests are currently in RED state (failing)
- Tests are ready for implementation in GREEN phase
- No implementation code exists yet
Test Naming Convention
Tests must follow the convention for traceability:
test_AC_{id}_{description}_{condition}
Examples:
test_AC_001_user_can_submit_valid_form
test_AC_001_submission_fails_with_empty_email
test_AC_002_success_message_shows_user_name
Test Quality Standards
Each test must:
- Have a single responsibility — test one behavior
- Be independent — no dependencies on other tests
- Use clear names — test name describes what's being tested
- Follow Arrange-Act-Assert pattern
- Arrange: Set up test data and mocks
- Act: Call the function or trigger the behavior
- Assert: Verify the outcome
- Include setup/teardown for dependencies
- Have descriptive error messages for assertions
- Be fast — unit tests in milliseconds, integration in seconds
Output Format
Upon completion, output:
# Test Writing Complete (RED Phase) ✓
## Feature
{feature-name} v{spec-version}
## Tests Generated
- Total Tests: {count}
- By Type:
- Unit Tests: {count}
- Integration Tests: {count}
- E2E Tests: {count}
- All in RED state (failing as expected)
## Acceptance Criteria Coverage
- AC-001: ✓ Covered by {N} tests
- AC-002: ✓ Covered by {N} tests
... (all ACs listed)
## Test Files Created
- {test-file-path}
- {test-file-path}
## Coverage Status
- Mapping file: tests/{feature}-mapping.md
- Ready for GREEN phase (implementation)
## Next Steps
1. Run /add-implementer to write minimal code to pass tests
2. Verify all tests pass
3. Proceed to REFACTOR phase
Progress Tracking
Tasks to create (mechanics per ~/.codex/add/references/skill-epilogue.md):
| Phase | Subject | activeForm |
|---|
| Parse spec | Reading spec and acceptance criteria | Reading spec and acceptance criteria... |
| Analyze framework | Analyzing test framework configuration | Analyzing test framework configuration... |
| Write tests | Writing failing tests | Writing failing tests... |
| Verify RED | Confirming tests fail as expected | Verifying tests fail (RED confirmed)... |
Error Handling
Spec parsing fails
- Verify spec YAML frontmatter is valid
- Check AC format matches
AC-###: description
- Halt and ask user to fix spec format
Test file already exists
- Append new tests to existing file
- Don't overwrite; preserve existing tests
- Report which tests are new
Tests don't fail when run
- This indicates implementation may already exist
- Verify the module path is correct
- If implementation exists, ask user if they want to:
- Proceed to GREEN/REFACTOR phases
- Or start fresh with new test file
Test framework not installed
- Provide guidance: "Install jest with: npm install --save-dev jest"
- Don't halt; user will install and retry
Syntax errors in generated tests
- Review the generated code carefully
- Fix imports, quotes, formatting
- Re-run tests to verify they compile
Integration with TDD Cycle
- This skill is invoked during the RED phase of /add-tdd-cycle
- Output becomes input to /add-implementer (GREEN phase)
- Test mapping is used by /add-reviewer to verify spec compliance
End-of-skill epilogue: follow ~/.codex/add/references/skill-epilogue.md (observation + learning checkpoint + progress tracking).