Files
dotfiles/.config/opencode/skills/test-driven-development/testing-anti-patterns.md

2.9 KiB

title, type, permalink
title type permalink
testing-anti-patterns note 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.