From zenflow
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.
How this skill is triggered — by the user, by Claude, or both
Slash command
/zenflow:testing-anti-patternsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Rule:** Test real behavior, not mock behavior. Never pollute production code with test-only methods.
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:
npx claudepluginhub brewpirate/zen-flow --plugin zenflowPrevents testing anti-patterns like verifying mock behavior, adding test-only methods to production code, and mocking without understanding dependencies. Use when writing, changing tests, or adding mocks.
Guides writing and reviewing tests with philosophy, Arrange-Act-Assert structure, condition-based waiting via polling, strategic mocking, and isolation principles.
Writes behavior-focused tests using Testing Trophy model: integration first with real dependencies, minimal mocking. For writing tests, choosing types, or avoiding anti-patterns.