
Testing Anti Patterns
Write a failing test first, watch it fail, then implement minimal code—never ship behavior without RED-GREEN-REFACTOR.
Install
npx skills add https://github.com/obra/superpowers-skills --skill testing-anti-patternsWhat is this skill?
- Iron law: delete production code written before the failing test exists.
- Mandatory verify-RED and verify-GREEN steps with real command output.
- Examples of good vs bad tests—real behavior, not over-mocked.
Adoption & trust: 73 installs on skills.sh; 692 GitHub stars; 3/3 security scanners passed (skills.sh audits).
Recommended Skills
Agent Browservercel-labs/open-agents
Tddmattpocock/skills
Use My Browserxixu-me/skills
Test Driven Developmentobra/superpowers
Verification Before Completionobra/superpowers
Webapp Testinganthropics/skills
Journey fit
Common Questions / FAQ
Is Testing Anti Patterns safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.
SKILL.md
READMESKILL.md - Testing Anti Patterns
# Testing Anti-Patterns ## Overview Tests must verify real behavior, not mock behavior. Mocks are a means to isolate, not the thing being tested. **Core principle:** Test what the code does, not what the mocks do. **Following strict TDD prevents these anti-patterns.** ## The Iron Laws ``` 1. NEVER test mock behavior 2. NEVER add test-only methods to production classes 3. NEVER mock without understanding dependencies ``` ## Anti-Pattern 1: Testing Mock Behavior **The violation:** ```typescript // ❌ BAD: Testing that the mock exists test('renders sidebar', () => { render(<Page />); expect(screen.getByTestId('sidebar-mock')).toBeInTheDocument(); }); ``` **Why this is wrong:** - You're verifying the mock works, not that the component works - Test passes when mock is present, fails when it's not - Tells you nothing about real behavior **your human partner's correction:** "Are we testing the behavior of a mock?" **The fix:** ```typescript // ✅ GOOD: Test real component or don't mock it test('renders sidebar', () => { render(<Page />); // Don't mock sidebar expect(screen.getByRole('navigation')).toBeInTheDocument(); }); // OR if sidebar must be mocked for isolation: // Don't assert on the mock - test Page's behavior with sidebar present ``` ### Gate Function ``` BEFORE asserting on any mock element: Ask: "Am I testing real component behavior or just mock existence?" IF testing mock existence: STOP - Delete the assertion or unmock the component Test real behavior instead ``` ## Anti-Pattern 2: Test-Only Methods in Production **The violation:** ```typescript // ❌ BAD: destroy() only used in tests class Session { async destroy() { // Looks like production API! await this._workspaceManager?.destroyWorkspace(this.id); // ... cleanup } } // In tests afterEach(() => session.destroy()); ``` **Why this is wrong:** - Production class polluted with test-only code - Dangerous if accidentally called in production - Violates YAGNI and separation of concerns - Confuses object lifecycle with entity lifecycle **The fix:** ```typescript // ✅ GOOD: Test utilities handle test cleanup // Session has no destroy() - it's stateless in production // In test-utils/ export async function cleanupSession(session: Session) { const workspace = session.getWorkspaceInfo(); if (workspace) { await workspaceManager.destroyWorkspace(workspace.id); } } // In tests afterEach(() => cleanupSession(session)); ``` ### Gate Function ``` BEFORE adding any method to production class: Ask: "Is this only used by tests?" IF yes: STOP - Don't add it Put it in test utilities instead Ask: "Does this class own this resource's lifecycle?" IF no: STOP - Wrong class for this method ``` ## Anti-Pattern 3: Mocking Without Understanding **The violation:** ```typescript // ❌ BAD: Mock breaks test logic test('detects duplicate server', () => { // Mock prevents config write that test depends on! vi.mock('ToolCatalog', () => ({ discoverAndCacheTools: vi.fn().mockResolvedValue(undefined) })); await addServer(config); await addServer(config); // Should throw - but won't! }); ``` **Why this is wrong:** - Mocked method had side effect test depended on (writing config) - Over-mocking to "be safe" breaks actual behavior - Test passes for wrong reason or fails mysteriously **The fix:** ```typescript // ✅ GOOD: Mock at correct level test('detects duplicate server', () => { // Mock the slow part, preserve behavior test needs vi.mock('MCPServerManager'); // Just mock slow server startup await addServer(config); // Config written await addServer(config); // Duplicate detected ✓ }); ``` ### Gate F