--- title: testing-anti-patterns type: note permalink: opencode-config/skills/test-driven-development/testing-anti-patterns --- # Testing Anti-Patterns Use this reference when writing/changing tests, introducing mocks, or considering test-only production APIs. ## Core Principle Test real behavior, not mock behavior. Mocks are isolation tools, not the subject under test. ## Anti-Pattern 1: Testing mock existence instead of behavior **Problem:** Assertions only prove a mock rendered or was called, not that business behavior is correct. **Fix:** Assert observable behavior of the unit/system under test. If possible, avoid mocking the component being validated. Gate check before assertions on mocked elements: - Am I validating system behavior or only that a mock exists? - If only mock existence, rewrite the test. ## Anti-Pattern 2: Adding test-only methods to production code **Problem:** Production classes gain methods used only by tests (cleanup hooks, debug helpers), polluting real APIs. **Fix:** Move test-only setup/cleanup into test utilities or fixtures. Gate check before adding a production method: - Is this method needed in production behavior? - Is this resource lifecycle actually owned by this class? - If not, keep it out of production code. ## Anti-Pattern 3: Mocking without understanding dependencies **Problem:** High-level mocks remove side effects the test depends on, causing false positives/negatives. **Fix:** Understand dependency flow first, then mock the lowest-cost external boundary while preserving needed behavior. Gate check before adding a mock: 1. What side effects does the real method perform? 2. Which side effects does this test rely on? 3. Can I mock a lower-level boundary instead? If unsure, run against real implementation first, then add minimal mocking. ## Anti-Pattern 4: Incomplete mock structures **Problem:** Mocks include only fields used immediately, omitting fields consumed downstream. **Fix:** Mirror complete response/object shapes used in real flows. Gate check for mocked data: - Does this mock match the real schema/shape fully enough for downstream consumers? - If uncertain, include the full documented structure. ## Anti-Pattern 5: Treating tests as a follow-up phase **Problem:** "Implementation complete, tests later" breaks TDD and reduces confidence. **Fix:** Keep tests inside the implementation loop: 1. Write failing test 2. Implement minimum code 3. Re-run tests 4. Refactor safely ## Quick Red Flags - Assertions target `*-mock` markers rather than behavior outcomes - Methods exist only for tests in production classes - Mock setup dominates test logic - You cannot explain why each mock is necessary - Tests are written only after code "already works" ## Bottom Line If a test does not fail first for the intended reason, it is not validating the behavior change reliably. Keep TDD strict: failing test first, then minimal code.