Prevent common testing anti-patterns that undermine test effectiveness and code quality by ensuring tests verify real behavior rather than mock behavior, keeping production code free from test-only pollution, and enforcing thoughtful mocking strategies. Use this skill when writing or modifying any test files (.test.ts, .test.js, .spec.ts, _test.py, test_*.py, *_test.go, *_spec.rb), when adding mock objects, stubs, spies, or test doubles to test suites, when considering adding methods or properties to production classes that are only called from test code, when setting up complex test fixtures or test data, when tests are failing and you're tempted to adjust mocks to make them pass, when deciding how to isolate code under test from external dependencies, when implementing dependency injection or test seams, during code reviews when reviewing test implementation and mocking strategies, when refactoring tests that have become brittle or hard to maintain, when test setup code is becoming longer than the actual test assertions, or when choosing between integration tests with real components versus unit tests with mocks.
From zenflownpx claudepluginhub brewpirate/zen-flow --plugin zenflowThis skill uses the workspace's default tool permissions.
Searches, retrieves, and installs Agent Skills from prompts.chat registry using MCP tools like search_skills and get_skill. Activates for finding skills, browsing catalogs, or extending Claude.
Searches prompts.chat for AI prompt templates by keyword or category, retrieves by ID with variable handling, and improves prompts via AI. Use for discovering or enhancing prompts.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
Rule: Test real behavior, not mock behavior. Never pollute production code with test-only methods.
expect(screen.getByTestId('component-mock')))Violation:
// ❌ BAD: Asserting on mock existence
test('renders sidebar', () => {
render(<Page />);
expect(screen.getByTestId('sidebar-mock')).toBeInTheDocument();
});
Why wrong:
Fix:
// ✅ GOOD: Test real component
test('renders sidebar', () => {
render(<Page />); // Use real sidebar
expect(screen.getByRole('navigation')).toBeInTheDocument();
});
// OR if isolation required: Test Page's behavior, not mock presence
test('renders page with sidebar slot', () => {
render(<Page />);
expect(screen.getByTestId('sidebar-container')).toBeInTheDocument();
});
Detection rule:
IF assertion contains '*-mock' OR checks mock.toHaveBeenCalled():
Ask: "Am I testing real behavior or mock existence?"
IF testing mock existence → STOP, delete assertion or unmock
Violation:
// ❌ BAD: Method only called from tests
class Session {
async destroy() {
await this._workspaceManager?.destroyWorkspace(this.id);
}
}
afterEach(() => session.destroy());
Why wrong:
Fix:
// ✅ GOOD: Test utilities handle cleanup
// Session class has no destroy() method
// In test-utils.ts
export async function cleanupSession(session: Session) {
const workspace = session.getWorkspaceInfo();
if (workspace) {
await workspaceManager.destroyWorkspace(workspace.id);
}
}
// In tests
afterEach(() => cleanupSession(session));
Detection rule:
BEFORE adding method to production class:
1. Search codebase: Is this method only called from test files?
2. Ask: Does this class own this resource's lifecycle?
IF only used in tests OR class doesn't own lifecycle:
STOP - Create test utility function instead
Violation:
// ❌ BAD: Mock removes side effect test depends on
test('detects duplicate server', () => {
vi.mock('ToolCatalog', () => ({
discoverAndCacheTools: vi.fn().mockResolvedValue(undefined)
}));
await addServer(config);
await addServer(config); // Should throw but won't - config never written!
});
Why wrong:
Fix:
// ✅ GOOD: Mock only external/slow operations
test('detects duplicate server', () => {
vi.mock('MCPServerManager'); // Mock slow server startup only
await addServer(config); // Config written ✓
await addServer(config); // Duplicate detected ✓
});
Decision process:
BEFORE mocking:
1. List method's side effects (DB writes, file I/O, API calls, state changes)
2. Identify what test actually needs (duplicate detection needs config write)
3. Mock ONLY external/slow operations, preserve test dependencies
IF unsure what test needs:
Run with real implementation FIRST
Observe required behavior
THEN mock minimally at lowest level
Red flags indicating wrong approach:
- "Mock this to be safe"
- "Might be slow, better mock"
- Can't explain why mocking
- Mock setup longer than test
Violation:
// ❌ BAD: Only fields you think you need
const mockResponse = {
status: 'success',
data: { userId: '123', name: 'Alice' }
// Missing: metadata field
};
// Later: Silent failure when code accesses response.metadata.requestId
Why wrong:
Fix:
// ✅ GOOD: Complete structure matching real API
const mockResponse = {
status: 'success',
data: { userId: '123', name: 'Alice' },
metadata: { requestId: 'req-789', timestamp: 1234567890 }
};
Mandatory process:
BEFORE creating mock data:
1. Check API documentation or real response examples
2. Include ALL fields from actual structure
3. Use realistic values (not null/undefined unless API returns them)
4. Verify mock matches real schema completely
IF uncertain about structure:
- Examine real API response
- Include all documented fields
- Add comment linking to API docs
Violation:
Implementation complete → No tests → "Ready for review"
Why wrong:
Fix - TDD cycle:
1. Write failing test (RED)
2. Implement minimal code (GREEN)
3. Refactor
4. THEN claim complete
Warning signs:
Question to ask: "Should this be an integration test with real components?"
Complex mocks often indicate integration tests would be simpler and more valuable.
TDD workflow naturally avoids anti-patterns:
Key insight: If you're testing mock behavior, you violated TDD by adding mocks before seeing test fail against real code.
Before finalizing any test, verify:
*-mock test IDs, toHaveBeenCalled without behavior verification)| Anti-Pattern | Detection Signal | Fix |
|---|---|---|
| Testing mock behavior | Assertions on *-mock elements or mock calls | Test real component or remove mock |
| Test-only methods | Method only in test file searches | Move to test utilities |
| Blind mocking | Can't explain mock purpose | Understand dependencies, mock minimally |
| Incomplete mocks | Missing fields from real structure | Include all documented fields |
| Tests afterthought | Implementation before tests | Follow TDD: test first |
| Over-complex mocks | Setup > 50% of test | Use integration test |
When you encounter these, stop and reassess your approach:
*-mock test IDsMocks isolate code under test from external dependencies. They are not the subject of tests.
When uncertain: