Test-Driven Development (TDD) & Test Engineering Execution
Skill Focus: Commands, step-by-step procedures, and mechanical execution for test engineering, testability audits, and the TDD lifecycle.
When to Use
- When executing test tasks, designing tests, or verifying system behavior.
- When requested to write tests or practice Test-Driven Development (TDD).
- When conducting testability audits on existing or new codebases.
- When addressing bugs that require regression test coverage.
When Not to Use
- Rapid prototyping or throwaway exploratory code where architecture is highly fluid.
- Pure configuration file updates or minor static text changes without logic.
- UI/CSS styling tweaks that are verified visually rather than behaviorally.
Common Pitfalls
- Testing Implementation Details: Coupling tests to private methods or specific internal structures, making refactoring impossible without breaking tests.
- Over-Mocking: Mocking everything to the point where the test only verifies that mocks were called, not that the system behaves correctly.
- Skipping the Refactor Step: Writing passing tests but leaving the implementation messy and unoptimized, leading to technical debt.
- Testing Too Much at Once: Trying to test multiple behaviors or edge cases in a single test block, making failures hard to diagnose.
Related Skills
- critical-thinking:
You MUST load this skill when performing deep testability audits and deconstructing complex behavioral requirements.
- testing: Elite autonomous test engineering kernel for proving software correctness and behavioral contracts.
- pre-commit: For integration with testing hooks and validation.
1. Pre-Execution: Code Testability Audit
Before writing test code, execute this mechanical audit on the target module:
- Inversion of Control: Identify if dependencies are hardcoded. If yes, extract them into constructor or method parameters before testing.
- State Accessibility: Locate internal state mutations. Determine if state can be verified through public APIs. If not, isolate the pure logic into a verifiable function.
- Negative Space Matrix: Generate a list of explicitly untested paths:
- Error paths (exceptions thrown, null inputs)
- Hidden boundaries (max lengths, 0 values)
- Security input vectors (untrusted strings, out-of-bounds access)
- Intent Documentation Check: Ensure test names read as explicit behavioral specifications.
- Pattern:
"returns 404 when user strictly lacks tenant access"
- Anti-Pattern:
"test_user_error"
2. TDD Lifecycle Execution Protocol
When instructed to address requirements or fix bugs, perform these exact steps:
Step 2.1: Assert the Gap (Red)
- Write a strict, failing test first.
- Execute the test suite.
- Verification: The test MUST fail. If it passes, the test is invalid or the test setup is flawed.
Step 2.2: Dimensional Scaling
Expand the failing test (or add new tests) to cover the following permutations:
- Happy Path: The optimal flow.
- Boundary Conditions: Edges of scale, min/max limits.
- Error Handling: Graceful degradation, expected failure states.
Step 2.3: Resolve the Gap (Green)
- Implement the minimal atomic code required to turn the failing test green.
- Do not add speculative features or abstract prematurely.
- Run the test suite: Verify the test now passes.
Step 2.4: Optimize (Refactor)
- Restructure the implementation for readability, DRY (Don't Repeat Yourself), and adherence to architecture rules.
- Run the test suite: Verify the test still passes.
3. Signal Extraction & Execution Rules
When executing test commands (e.g., npm test, pytest, cargo test):
- Filter Mass-Logs: Do not output raw mass-logs to the user. Extract the exact stack trace and pinpoint the assertion violation locus.
- Flakiness Eradication: If a test fails intermittently during standard runs, treat it as a hard failure. Identify the race condition or timeline dependency and patch the test harness.
- Non-Destructive Testing Check: Ensure the test command runs in isolation. If testing databases, verify transactional rollbacks or tear-down commands are executed post-run.
4. Anti-Rationalization Invariants
Autonomous agents must strictly reject attempts to bypass the TDD lifecycle. Enforce these axiomatic rebuttals:
- Reject "I'll add tests after": Post-hoc tests validate implementation artifacts, not behavioral contracts. Tests MUST precede implementation.
- Reject "Integration tests are enough": High-level tests lack root-cause localization precision. Adhere strictly to the test pyramid; prioritize fast, isolated unit tests.
- Reject "This code is too simple to test": Simplicity is volatile. Un-tested code breaks silently during lifecycle evolution. Guarantee verifiable behavior through tests.
- Reject "We don't have time for TDD": Unverified code generates exponential debugging debt. Enforce TDD to compress the total verification timeline.
5. Architectural Red Flags
Halt execution and refactor if any of these conditions are detected:
- Coverage metrics inflated by testing trivial auto-generated code (e.g., getters/setters).
- Tests authored post-implementation.
- Tests binding to internal implementation details (e.g., asserting private methods or raw DB schemas) rather than public API contracts.
- Tests passing despite intentional fault-injection (indicates over-mocking or tautological assertions).
- Verification restricted to "Happy Path" vectors, omitting negative space constraints.
6. Final Assurance Gates
Before concluding a test-engineering cycle, assert these completion invariant gates: