Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Enforce strict Red-Green-Refactor TDD cycle—no production code written before a failing test exists
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
testing-anti-patterns.md
1# Testing Anti-Patterns23**Load this reference when:** writing or changing tests, adding mocks, or tempted to add test-only methods to production code.45## Overview67Tests must verify real behavior, not mock behavior. Mocks are a means to isolate, not the thing being tested.89**Core principle:** Test what the code does, not what the mocks do.1011**Following strict TDD prevents these anti-patterns.**1213## The Iron Laws1415```161. NEVER test mock behavior172. NEVER add test-only methods to production classes183. NEVER mock without understanding dependencies19```2021## Anti-Pattern 1: Testing Mock Behavior2223**The violation:**24```typescript25// ❌ BAD: Testing that the mock exists26test('renders sidebar', () => {27render(<Page />);28expect(screen.getByTestId('sidebar-mock')).toBeInTheDocument();29});30```3132**Why this is wrong:**33- You're verifying the mock works, not that the component works34- Test passes when mock is present, fails when it's not35- Tells you nothing about real behavior3637**your human partner's correction:** "Are we testing the behavior of a mock?"3839**The fix:**40```typescript41// ✅ GOOD: Test real component or don't mock it42test('renders sidebar', () => {43render(<Page />); // Don't mock sidebar44expect(screen.getByRole('navigation')).toBeInTheDocument();45});4647// OR if sidebar must be mocked for isolation:48// Don't assert on the mock - test Page's behavior with sidebar present49```5051### Gate Function5253```54BEFORE asserting on any mock element:55Ask: "Am I testing real component behavior or just mock existence?"5657IF testing mock existence:58STOP - Delete the assertion or unmock the component5960Test real behavior instead61```6263## Anti-Pattern 2: Test-Only Methods in Production6465**The violation:**66```typescript67// ❌ BAD: destroy() only used in tests68class Session {69async destroy() { // Looks like production API!70await this._workspaceManager?.destroyWorkspace(this.id);71// ... cleanup72}73}7475// In tests76afterEach(() => session.destroy());77```7879**Why this is wrong:**80- Production class polluted with test-only code81- Dangerous if accidentally called in production82- Violates YAGNI and separation of concerns83- Confuses object lifecycle with entity lifecycle8485**The fix:**86```typescript87// ✅ GOOD: Test utilities handle test cleanup88// Session has no destroy() - it's stateless in production8990// In test-utils/91export async function cleanupSession(session: Session) {92const workspace = session.getWorkspaceInfo();93if (workspace) {94await workspaceManager.destroyWorkspace(workspace.id);95}96}9798// In tests99afterEach(() => cleanupSession(session));100```101102### Gate Function103104```105BEFORE adding any method to production class:106Ask: "Is this only used by tests?"107108IF yes:109STOP - Don't add it110Put it in test utilities instead111112Ask: "Does this class own this resource's lifecycle?"113114IF no:115STOP - Wrong class for this method116```117118## Anti-Pattern 3: Mocking Without Understanding119120**The violation:**121```typescript122// ❌ BAD: Mock breaks test logic123test('detects duplicate server', () => {124// Mock prevents config write that test depends on!125vi.mock('ToolCatalog', () => ({126discoverAndCacheTools: vi.fn().mockResolvedValue(undefined)127}));128129await addServer(config);130await addServer(config); // Should throw - but won't!131});132```133134**Why this is wrong:**135- Mocked method had side effect test depended on (writing config)136- Over-mocking to "be safe" breaks actual behavior137- Test passes for wrong reason or fails mysteriously138139**The fix:**140```typescript141// ✅ GOOD: Mock at correct level142test('detects duplicate server', () => {143// Mock the slow part, preserve behavior test needs144vi.mock('MCPServerManager'); // Just mock slow server startup145146await addServer(config); // Config written147await addServer(config); // Duplicate detected ✓148});149```150151### Gate Function152153```154BEFORE mocking any method:155STOP - Don't mock yet1561571. Ask: "What side effects does the real method have?"1582. Ask: "Does this test depend on any of those side effects?"1593. Ask: "Do I fully understand what this test needs?"160161IF depends on side effects:162Mock at lower level (the actual slow/external operation)163OR use test doubles that preserve necessary behavior164NOT the high-level method the test depends on165166IF unsure what test depends on:167Run test with real implementation FIRST168Observe what actually needs to happen169THEN add minimal mocking at the right level170171Red flags:172- "I'll mock this to be safe"173- "This might be slow, better mock it"174- Mocking without understanding the dependency chain175```176177## Anti-Pattern 4: Incomplete Mocks178179**The violation:**180```typescript181// ❌ BAD: Partial mock - only fields you think you need182const mockResponse = {183status: 'success',184data: { userId: '123', name: 'Alice' }185// Missing: metadata that downstream code uses186};187188// Later: breaks when code accesses response.metadata.requestId189```190191**Why this is wrong:**192- **Partial mocks hide structural assumptions** - You only mocked fields you know about193- **Downstream code may depend on fields you didn't include** - Silent failures194- **Tests pass but integration fails** - Mock incomplete, real API complete195- **False confidence** - Test proves nothing about real behavior196197**The Iron Rule:** Mock the COMPLETE data structure as it exists in reality, not just fields your immediate test uses.198199**The fix:**200```typescript201// ✅ GOOD: Mirror real API completeness202const mockResponse = {203status: 'success',204data: { userId: '123', name: 'Alice' },205metadata: { requestId: 'req-789', timestamp: 1234567890 }206// All fields real API returns207};208```209210### Gate Function211212```213BEFORE creating mock responses:214Check: "What fields does the real API response contain?"215216Actions:2171. Examine actual API response from docs/examples2182. Include ALL fields system might consume downstream2193. Verify mock matches real response schema completely220221Critical:222If you're creating a mock, you must understand the ENTIRE structure223Partial mocks fail silently when code depends on omitted fields224225If uncertain: Include all documented fields226```227228## Anti-Pattern 5: Integration Tests as Afterthought229230**The violation:**231```232✅ Implementation complete233❌ No tests written234"Ready for testing"235```236237**Why this is wrong:**238- Testing is part of implementation, not optional follow-up239- TDD would have caught this240- Can't claim complete without tests241242**The fix:**243```244TDD cycle:2451. Write failing test2462. Implement to pass2473. Refactor2484. THEN claim complete249```250251## When Mocks Become Too Complex252253**Warning signs:**254- Mock setup longer than test logic255- Mocking everything to make test pass256- Mocks missing methods real components have257- Test breaks when mock changes258259**your human partner's question:** "Do we need to be using a mock here?"260261**Consider:** Integration tests with real components often simpler than complex mocks262263## TDD Prevents These Anti-Patterns264265**Why TDD helps:**2661. **Write test first** → Forces you to think about what you're actually testing2672. **Watch it fail** → Confirms test tests real behavior, not mocks2683. **Minimal implementation** → No test-only methods creep in2694. **Real dependencies** → You see what the test actually needs before mocking270271**If you're testing mock behavior, you violated TDD** - you added mocks without watching test fail against real code first.272273## Quick Reference274275| Anti-Pattern | Fix |276|--------------|-----|277| Assert on mock elements | Test real component or unmock it |278| Test-only methods in production | Move to test utilities |279| Mock without understanding | Understand dependencies first, mock minimally |280| Incomplete mocks | Mirror real API completely |281| Tests as afterthought | TDD - tests first |282| Over-complex mocks | Consider integration tests |283284## Red Flags285286- Assertion checks for `*-mock` test IDs287- Methods only called in test files288- Mock setup is >50% of test289- Test fails when you remove mock290- Can't explain why mock is needed291- Mocking "just to be safe"292293## The Bottom Line294295**Mocks are tools to isolate, not things to test.**296297If TDD reveals you're testing mock behavior, you've gone wrong.298299Fix: Test real behavior or question why you're mocking at all.300