tests-creator
Use when writing test cases BEFORE implementation — converts acceptance criteria into failing test stubs that task-implementer will make pass. Mandatory step in the TDD pipeline between issue-analyzer and task-implementer.
Tests Creator
Purpose
Converts acceptance criteria (from issue-analyzer or manual input) into concrete, failing test stubs before any implementation code is written. Enforces the RED phase of TDD.
Input: Task object with acceptance_criteria + codebase path + test framework info
Output: Ready-to-run test files with failing test stubs + test_case_specs block for task-implementer
When to Use
- Between
issue-analyzerandtask-implementerin the TDD pipeline - Orchestrator needs test specs before dispatching implementation
- User wants to review test expectations before code is written
- Acceptance criteria need to be translated into executable specifications
Architecture: 4 Phases
Phase 1: DETECT → Identify test framework, conventions, fixture patterns
Phase 2: ANALYZE → Map acceptance criteria to test scenarios
Phase 3: GENERATE → Write failing test stubs with correct framework syntax
Phase 4: REPORT → Emit test_case_specs for task-implementer consumption
Workflow
Tests Creator Progress:
- [ ] Phase 1: Detect test framework and conventions
- [ ] Phase 2: Map acceptance criteria to test scenarios
- [ ] Phase 3: Generate failing test stubs
- [ ] Phase 4: Report test_case_specs
Phase 1: DETECT
Identify the test framework and conventions used in the project.
1.1 Detect framework:
# Check package.json for test dependencies
cat <codebase_path>/package.json | grep -E '"(jest|vitest|mocha|jasmine|bun:test|pytest|go test)"'
# Check for config files
ls <codebase_path>/{vitest.config.*,jest.config.*,pytest.ini,setup.cfg}
# Check existing test files for imports
find <codebase_path>/src -name "*.test.*" -o -name "*.spec.*" | head -5
1.2 Read 2-3 existing test files to understand:
- Import style (
import { describe, it, expect } from 'vitest'vs global) - Test file location (co-located
*.test.tsvs__tests__/directory) - Describe/it/test nesting patterns
- Common assertion patterns (
expect(x).toBe(y)vsassert.equal(x, y)) - Mock patterns (
vi.fn()vsjest.fn()vs manual mocks) - Fixture/factory patterns (how test data is created)
- Before/after hook usage
1.3 Identify test file naming:
- Pattern:
<component>.test.ts,<service>.spec.ts,test_<module>.py, etc. - Location: co-located with source or in
__tests__/tests/directory
Output of Phase 1:
TEST_ENV:
framework: vitest | jest | bun:test | mocha | pytest | go_test
import_style: esm | cjs | global
file_pattern: "*.test.ts" | "*.spec.ts" | "test_*.py"
file_location: co-located | __tests__ | tests/
assertion_style: expect | assert | chai
mock_library: vi | jest | sinon | unittest.mock
fixture_pattern: <description of how test data is created>
Phase 2: ANALYZE
Map each acceptance criterion to one or more test scenarios.
2.1 Parse acceptance criteria:
For each criterion, determine:
- Happy path: when everything works correctly
- Edge cases: boundary values, empty inputs, maximum values
- Error paths: invalid input, missing data, permission denied, external failure
2.2 Criterion-to-scenario mapping:
| Criterion Type | Test Scenarios to Generate |
|---|---|
| "User can X" | happy path: user can X; error: user cannot X when [condition] |
| "System returns Y when Z" | given Z → returns Y; given not-Z → does not return Y |
| "Field is required" | valid data passes; missing field fails with error |
| "Value must be between A and B" | A passes; B passes; A-1 fails; B+1 fails |
| "X must not happen" | verify X does not happen under [conditions] |
| "Async operation completes" | resolves with expected value; rejects on failure |
2.3 Group scenarios by test file:
Group related scenarios into test files matching the target_files from the task:
- Service test → test the service class methods
- Component test → test component rendering and interactions
- Integration test → test the full flow
Output of Phase 2:
TEST_PLAN:
- test_file: <path>
target_module: <source file being tested>
scenarios:
- id: test-1
criterion: <which acceptance criterion>
description: <what to test>
type: happy_path | edge_case | error_path
setup: <what to arrange>
action: <what to call/render/trigger>
assertion: <what to expect>
Phase 3: GENERATE
Write the actual test files with failing stubs.
3.1 Test file structure:
// Template for TypeScript (vitest/jest)
import { describe, it, expect, vi, beforeEach } from 'vitest';
// import the module under test — may not exist yet, that is expected
import { <ModuleUnderTest> } from '<relative path>';
describe('<ModuleUnderTest>', () => {
// Acceptance criterion: <criterion text>
describe('<group name>', () => {
it('<should do X when Y>', async () => {
// Arrange
const <fixture> = <test data>;
// Act
const result = await <action>;
// Assert
expect(result).<assertion>;
});
it('<should fail when Z>', async () => {
// Arrange
const <invalid fixture> = <invalid data>;
// Act & Assert
await expect(<action with invalid data>).rejects.toThrow(<error type>);
});
});
});
3.2 Failing stubs (RED phase):
The generated tests MUST:
- Import the module under test (file may not exist yet — that is fine)
- Have meaningful test descriptions
- Have placeholder assertions that will FAIL until implementation:
// Use todo() for unimplemented tests it.todo('<test description>'); // OR use a stub assertion that fails it('<test description>', () => { expect(true).toBe(false); // RED: remove this when implementing }); - OR use proper assertions calling the future API (will fail with "module not found" or "function is not a function")
3.3 Preferred approach — forward-declared tests:
Write tests that call the actual future API with real assertions. They will fail because the module doesn't exist yet:
// This file will fail to compile/run until task-implementer creates:
// src/services/UserValidator.ts with a validate() method
import { UserValidator } from '../services/UserValidator';
describe('UserValidator', () => {
it('should accept valid email address', () => {
const validator = new UserValidator();
expect(validator.validate({ email: '[email protected]' })).toEqual({ valid: true });
});
it('should reject email without @ symbol', () => {
const validator = new UserValidator();
expect(validator.validate({ email: 'not-an-email' })).toEqual({
valid: false,
errors: [{ field: 'email', message: 'Invalid email format' }],
});
});
});
3.4 Commit the test files:
git add <test files>
git commit -m "test(<scope>): add failing test stubs for <task description>
RED phase — tests will pass after implementation
refs #<issue_number>"
Phase 4: REPORT
Emit the test_case_specs block for consumption by task-implementer.
Output structure:
TEST_CASE_SPECS:
framework: <vitest|jest|pytest|...>
test_files:
- path: <test file path>
target_module: <source file to implement>
test_count: <N>
tests:
- id: test-1
description: <it description>
criterion: <which acceptance criterion>
type: happy_path | edge_case | error_path
status: written # test file exists, test is RED
run_command: "<command to run just these tests>"
expected_result: "all failing (RED phase)"
notes: "<any test design decisions or assumptions>"
STATUS reporting:
STATUS: DONE
tests_written: <N>
test_files: [<list of paths>]
all_criteria_covered: true | false (with explanation)
Integration with Task Implementer
When task-implementer receives a task that includes test_case_specs:
- Read the test files (already committed as RED)
- Run the tests — confirm they FAIL
- Implement code until all tests are GREEN
- Commit the implementation
- Re-run tests — confirm GREEN
- Report SUCCESS
This ensures the TDD cycle is maintained end-to-end.
Automation Settings
| Setting | Default | Options | Description |
|---|---|---|---|
commit_test_stubs | true | true/false | Commit the test files after generation |
verify_red | true | true/false | Run tests to confirm they fail before reporting |
stub_style | forward_declared | forward_declared / todo | How to write failing stubs |
include_edge_cases | true | true/false | Generate edge case tests in addition to happy path |
max_tests_per_criterion | 3 | 1-5 | Max test cases per acceptance criterion |
Error Handling
| Error | Action |
|---|---|
| No acceptance criteria provided | Derive from task description — log warning |
| Test framework not detected | Ask via output, default to vitest for TypeScript |
| Test file already exists | Read existing tests, add new stubs without overwriting |
| Module path unknown | Use placeholder path, note in test_case_specs |
| Verify-red fails (tests pass before implementation) | This means the test is wrong — fix the assertion or report as concern |
Rules of Engagement
- DO NOT write any implementation code. This skill only writes test files.
- DO NOT write tests that pass without implementation — RED means failing.
- DO write tests that describe WHAT the code should do, not HOW.
- DO cover every acceptance criterion with at least one test.
- DO follow the project's existing test conventions (discovered in Phase 1).
- DO commit the test files before reporting.
- Return
TEST_CASE_SPECSas the final message to the orchestrator/caller.
Job Context Awareness
When dispatched by job-orchestrator:
JOB_NAME: <job-name>
CONTEXT_PATH: <JOBS_ROOT>/<job-name>/ai/context.md
If provided, read the context document to understand:
- Which test frameworks are in use
- Testing conventions and patterns from the codebase
- Mock/stub strategies documented in the project