home / skills / doanchienthangdev / omgkit / testing-anti-patterns
This skill helps you identify and fix testing anti-patterns to reduce flakiness, speed up tests, and improve maintainability.
npx playbooks add skill doanchienthangdev/omgkit --skill testing-anti-patternsReview the files below or copy the command above to add this skill to your agents.
---
name: avoiding-testing-anti-patterns
description: AI agent identifies and fixes common testing anti-patterns that lead to flaky, slow, or unmaintainable test suites. Use when reviewing tests, debugging test failures, or improving test quality.
---
# Avoiding Testing Anti-Patterns
## Quick Start
1. **Identify** - Recognize anti-pattern category (flaky, implementation, over-mocking)
2. **Assess Severity** - Critical (fix now), High (fix soon), Medium (plan to fix)
3. **Apply Fix** - Use proper async handling, test behavior not implementation
4. **Verify** - Run tests in random order, ensure independence
5. **Prevent** - Add test smell detection to CI
## Features
| Feature | Description | Guide |
|---------|-------------|-------|
| Flaky Tests | Random failures destroying trust | Use waitFor, not sleep; deterministic data |
| Implementation Testing | Breaks on every refactor | Test behavior through public interface |
| Over-Mocking | Tests pass but bugs slip through | Mock boundaries only, not your own code |
| Slow Tests | Hurt development velocity | Right test layer, shared setup, mock network |
| Test Interdependence | Can't run tests in isolation | Fresh state in beforeEach, no shared mutation |
| Poor Design | Hard to understand/maintain | Descriptive names, focused tests, clear values |
## Common Patterns
```typescript
// FLAKY: Timing-dependent
await sleep(100); // May not be enough
expect(result).toBe('processed');
// FIXED: Wait for condition
await waitFor(() => {
expect(result).toBe('processed');
}, { timeout: 5000 });
// FLAKY: Shared state between tests
let sharedState = [];
it('test1', () => { sharedState.push('a'); });
it('test2', () => { expect(sharedState).toHaveLength(1); }); // Order-dependent!
// FIXED: Fresh state each test
beforeEach(() => { sharedState = []; });
// IMPLEMENTATION: Testing private state
expect(counter._count).toBe(1);
// BEHAVIOR: Testing public interface
expect(counter.getValue()).toBe(1);
// OVER-MOCKING: Everything mocked
const mockDb = { save: jest.fn() };
const mockPayment = { charge: jest.fn() };
// Only testing that mocks were called
// FIXED: Mock boundaries only
const testDb = await createTestDatabase(); // Real
const mockPayment = createMockPaymentProvider(); // External only
```
```
# Anti-Pattern Severity Guide
CRITICAL (fix immediately):
- Flaky tests - Random failures destroy trust
- Testing implementation - Breaks on every refactor
- Hidden dependencies - Tests fail mysteriously
HIGH (fix soon):
- Slow tests - Hurt development velocity
- Test interdependence - Can't run in isolation
- Over-mocking - Tests pass but bugs slip through
MEDIUM (plan to fix):
- Poor naming - Tests don't document behavior
- Magic values - Unclear expected values
- Giant tests - Hard to understand
LOW (fix when touching):
- Commented tests - Remove or fix
- Duplicate tests - Consolidate
```
## Best Practices
| Do | Avoid |
|----|-------|
| Test behavior, not implementation | Accessing private properties |
| Use factories for test data | Random/inconsistent test data |
| Write descriptive test names | Generic names like "test1", "should work" |
| Keep tests independent | Shared mutable state between tests |
| Mock only external boundaries | Mocking your own code extensively |
| Use waitFor, not sleep | setTimeout/sleep in tests |
| Make assertions specific | toBeDefined() for everything |
| Run tests in random order | Assuming test execution order |
## Related Skills
- `developing-test-driven` - TDD with proper patterns
- `testing-with-vitest` - Vitest testing framework
- `testing-with-playwright` - E2E testing patterns
- `debugging-systematically` - Debug flaky test failures
This skill identifies and fixes common testing anti-patterns that cause flaky, slow, or unmaintainable test suites. It guides you through triage, concrete fixes, and prevention steps so tests remain reliable and fast. Use it during test reviews, debugging, or when improving test quality across a JavaScript codebase.
The skill inspects test code for known smells: timing-dependent waits, shared mutable state, over-mocking, implementation-focused assertions, and slow layers. It recommends severity-rated fixes and concrete replacements (waitFor instead of sleep, fresh setup in beforeEach, mock external boundaries only). It also suggests CI checks and randomized runs to verify independence and prevent regressions.
How do I prioritize which test anti-patterns to fix first?
Focus on critical issues first: flaky tests, implementation testing, and hidden dependencies. These destroy trust and block development. Next, address high-severity problems like slow tests and excessive mocking.
What if fixing an anti-pattern requires large refactors?
Triage by impact and scope: apply small, incremental fixes that stabilize behavior (isolate failing tests, add fresh fixtures), then schedule larger refactors. Use CI checks to prevent regressions as you refactor.