Tdd discipline
Skill Inteligentsensingsolutions/tdd-dev-workflow/skills/tdd-discipline
This skill should be used when the user asks about "TDD", "test-driven development", "red green refactor", "write tests first", "failing test", "test first", "TDD cycle", "test coverage", "how to start implementing", "implement a feature", "add tests for", or needs guidance on test-driven development methodology. Enforces the red-green-refactor discipline.From its SKILL.md
npx -y skills add Inteligentsensingsolutions/tdd-dev-workflow --skill tdd-disciplineAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
9.8 KB, as published. Nobody here has run it
TDD Discipline: Red-Green-Refactor
The Iron Law
NO PRODUCTION CODE WITHOUT A FAILING TEST FIRST.
No exceptions. No "just this once." No "it's too simple to test." No "I'll add tests later." Every line of production code must exist because a test demanded it.
This is not a guideline. This is a discipline.
The TDD Cycle
The cycle has exactly three phases. Execute them in strict order, every time.
RED ---> GREEN ---> REFACTOR ---> RED ---> ...
Phase 1: RED -- Write a Failing Test
Goal: Define the expected behavior before writing any implementation.
Steps
- Identify the next smallest piece of behavior to implement.
- Write a single test that asserts that behavior.
- Run the test. It MUST fail.
- Verify the failure message. The test must fail for the right reason.
What "Failing for the Right Reason" Means
- A test that fails because the function does not exist yet: correct.
- A test that fails because the assertion does not match the return value: correct.
- A test that fails because of a syntax error in the test: incorrect. Fix the test.
- A test that fails because of a missing import: incorrect. Fix the test.
The failure message must describe the behavioral gap, not an infrastructure problem.
Rules
- One logical assertion per test. Test one behavior at a time.
- Name the test to describe the behavior, not the implementation.
- Do not write multiple failing tests at once. One RED, one GREEN, then repeat.
- If unsure what to test next, revisit the acceptance criteria.
Example
describe('TemperatureConverter', () => {
it('converts 0 Celsius to 32 Fahrenheit', () => {
expect(convertCelsiusToFahrenheit(0)).toBe(32);
});
});
Run it. See it fail: convertCelsiusToFahrenheit is not defined. This is the right reason.
Phase 2: GREEN -- Make the Test Pass
Goal: Write the minimum code necessary to make the failing test pass.
Steps
- Write the simplest implementation that makes the test pass.
- Run the test. It MUST pass.
- Run all existing tests. They must ALL still pass.
- Stop.
What "Minimum Code" Means
- If a hardcoded return value makes the test pass, that is valid. The next test will force generalization.
- Do not add error handling that no test requires.
- Do not add features that no test requires.
- Do not optimize code that no test requires to be optimized.
- Do not refactor yet. That is the next phase.
The Temptation Rule
If tempted to write more code than the test demands, STOP. Write another test first. Let the tests drive the design.
Example
function convertCelsiusToFahrenheit(celsius: number): number {
return celsius * 9/5 + 32;
}
Run the test. Green. All existing tests still green. Stop.
Phase 3: REFACTOR -- Improve the Code
Goal: Improve code structure and quality while all tests remain green.
Steps
- Look for duplication, poor naming, complex conditionals, long functions.
- Improve the code in small increments.
- Run all tests after each change. They MUST stay green.
- If tests break during refactoring, undo the last change immediately.
What to Refactor
- Remove duplication: Extract repeated logic into shared functions or constants.
- Improve naming: Rename variables, functions, and classes to express intent.
- Extract patterns: Pull out helper functions, modules, or classes.
- Simplify conditionals: Replace nested if/else with early returns or polymorphism.
- Reduce coupling: Move toward cleaner interfaces and better separation of concerns.
What NOT to Do During Refactoring
- Do not add new functionality. That requires a new RED phase.
- Do not change the external behavior of the code. Tests must stay green.
- Do not skip running tests between refactoring steps.
Test Structure: Arrange-Act-Assert
Structure every test with three clear sections:
it('calculates total price with tax', () => {
// Arrange: set up preconditions and inputs
const cart = new ShoppingCart();
cart.addItem({ name: 'Widget', price: 10.00 });
const taxRate = 0.08;
// Act: execute the behavior under test
const total = cart.calculateTotal(taxRate);
// Assert: verify the expected outcome
expect(total).toBe(10.80);
});
Rules
- Keep each section small and focused.
- One Act per test. If multiple actions are needed, consider splitting into multiple tests.
- Use descriptive test names that document the behavior being verified.
- Group related tests with
describeblocks to provide context.
Naming Convention
Name tests to describe behavior from the user's perspective:
// Good: describes behavior
'returns empty array when no items match filter'
'throws validation error when email format is invalid'
'sends notification after successful payment'
// Bad: describes implementation
'calls filterItems method'
'checks regex pattern'
'triggers sendEmail function'
What to Test
Coverage Strategy
For each acceptance criterion in an issue, write at least one test. Then extend coverage:
| Category | Description | Example |
|---|---|---|
| Happy path | Normal expected usage | Valid input produces correct output |
| Edge cases | Boundary and unusual inputs | Empty string, zero, max value |
| Error conditions | Invalid inputs and failure modes | Null input, network timeout |
| Boundary values | Values at the edges of valid ranges | First item, last item, off-by-one |
Test Ordering
Start with the simplest happy path test. Build complexity incrementally:
- Simplest possible input and output.
- Another variation of the happy path.
- Edge case: empty or minimal input.
- Edge case: boundary values.
- Error condition: invalid input.
- Error condition: external failure.
Each test adds one new dimension of behavior. The implementation grows organically to handle each new case.
Anti-Patterns to Avoid
Testing Implementation Details
Do NOT test how something is done internally. Test what it does.
// Bad: tests implementation detail
expect(service.internalCache.size).toBe(3);
// Good: tests observable behavior
expect(service.getItems()).toHaveLength(3);
If the internal implementation changes but behavior stays the same, tests should not break.
Writing Tests After Code
Writing tests after code turns them into a rubber stamp. The tests confirm what was already written rather than driving the design. Always write the test first.
Skipping the RED Phase
Running a test that passes immediately provides no information. If the test passes without new production code, either the test is wrong (it does not test what it should) or the behavior already exists (no new code needed).
Writing Too Many Tests at Once
Write one test, make it pass, refactor. Then write the next test. Batching tests leads to large jumps in implementation and missed design feedback.
Testing Trivial Code
The iron law applies to code containing decisions, transformations, or side effects. Simple getters, setters, or constructor assignments with no logic are not production behavior that requires a failing test -- they are structural wiring. Focus testing effort on code that makes decisions, transforms data, or produces side effects.
When to Apply TDD
Features
Apply the full Red-Green-Refactor cycle. Start from the acceptance criteria and work through each criterion as a test.
Bug Fixes
- Write a failing test that reproduces the bug. This test captures the exact broken behavior.
- Verify it fails for the right reason (the bug).
- Fix the code.
- Verify the test passes.
- This test now prevents the bug from recurring.
Refactoring
- Ensure comprehensive tests exist before changing any code.
- If tests are missing, add them first (characterization tests).
- Refactor in small steps, running tests after each change.
- Never refactor and add features simultaneously.
TDD Decision Checklist
Before writing any production code, confirm:
- A failing test exists that demands this code.
- The test fails for the right reason.
- The test name describes a behavior, not an implementation.
- Only the minimum code to pass the test is being written.
- All existing tests still pass.
- Refactoring is complete before writing the next test.
Quick Reference
| Phase | Action | Validation |
|---|---|---|
| RED | Write one failing test | Test fails for the right reason |
| GREEN | Write minimum code to pass | All tests green |
| REFACTOR | Improve structure, no new behavior | All tests stay green |
Dependencies
None -- this is a foundation workflow skill.
Used By
| Component | Reason |
|---|---|
issue-workflow | Step 4 mandates TDD for all implementation work |
systematic-debugging | Phase 4 uses TDD to implement the fix |
e2e-testing | E2E tests run after the TDD unit/integration cycle |
Agent: tdd-red | Executes the RED phase |
Agent: tdd-green | Executes the GREEN phase |
Agent: tdd-refactor | Executes the REFACTOR phase |