Reviews test code to identify and fix common testing anti-patterns including flaky tests, over-mocking, brittle assertions, test interdependency, and hidden test logic. Flags bad patterns, explains the specific defect, and provides corrected implementations. Use when reviewing test code, debugging intermittent or unreliable test failures, or when the user mentions flaky tests, test smells, brittle tests, test isolation issues, mock overuse, slow tests, or test maintenance problems.
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Reviews test code to identify and fix common testing anti-patterns including flaky tests, over-mocking, brittle assertions, test interdependency, and hidden test logic. Flags bad patterns, explains the specific defect, and provides corrected implementations. Use when reviewing test code, debugging intermittent or unreliable test failures, or when the user mentions flaky tests, test smells, brittle tests, test isolation issues, mock overuse, slow tests, or test maintenance problems.
You are identifying and avoiding common testing anti-patterns.
Review Workflow
Follow these steps when reviewing test code:
Run tests in isolation — Verify each test passes independently (no shared state, no ordering dependency).
Check for patterns below — Scan for each anti-pattern in the checklist; flag every match with the specific defect.
Apply refactoring strategy — Use the refactoring strategies section to select and apply the appropriate fix.
Verify the test still fails when code breaks — After fixing, confirm the corrected test catches real regressions (remove or stub the implementation to confirm a failure occurs).
Critical Anti-Patterns
1. The Liar - Tests That Always Pass
Problem: Test passes even when the code is broken.
// BAD - Always passes because it tests nothing meaningfulit('should process data', () => {
const result = processData(input);
expect(result).toBeDefined(); // Too weak
});
// GOOD - Actually verifies behaviorit('should transform input to uppercase', () => {
const result = processData({ text: 'hello' });
expect(result.text).toBe('HELLO');
});
Detection: Remove or break the implementation - test should fail.
// BAD - Everything is mocked, test proves nothingit('should calculate price', () => {
const mockProduct = { getPrice: jest.fn().mockReturnValue(100) };
const mockDiscount = { apply: jest.fn().mockReturnValue(80) };
const mockTax = { calculate: jest.fn().mockReturnValue(8) };
const total = calculateTotal(mockProduct, mockDiscount, mockTax);
expect(total).toBe(88); // Just testing mock arithmetic
});
// GOOD - Use real objects where feasibleit('should apply 20% discount to price', () => {
const product = newProduct({ price: 100 });
const discount = newPercentageDiscount(20);
const total = calculateTotal(product, discount);
expect(total).toBe(80);
});
Fix: Only mock external dependencies and side effects.
5. The Flaky Test - Random Failures
Problem: Test sometimes passes, sometimes fails.
Common causes:
Time-dependent logic
Race conditions in async code
Shared mutable state
External dependencies
// BAD - Depends on current timeit('should show recent items', () => {
const item = { createdAt: newDate() };
expect(isRecent(item)).toBe(true);
});
// GOOD - Control the timeit('should show items from last 24 hours', () => {
const now = newDate('2024-01-15T12:00:00Z');
jest.setSystemTime(now);
const recent = { createdAt: newDate('2024-01-15T00:00:00Z') };
const old = { createdAt: newDate('2024-01-13T00:00:00Z') };
expect(isRecent(recent)).toBe(true);
expect(isRecent(old)).toBe(false);
});
6. The Slow Poke - Unnecessarily Slow Tests
Problem: Tests take too long to run.
// BAD - Real network callit('should fetch user data', async () => {
const response = awaitfetch('https://api.example.com/users/1');
const user = await response.json();
expect(user.name).toBeDefined();
});
// GOOD - Mocked networkit('should parse user response', async () => {
mockFetch.mockResolvedValue({
json: () =>Promise.resolve({ id: 1, name: 'Test User' })
});
const user = awaitfetchUser(1);
expect(user.name).toBe('Test User');
});
Target: Unit tests < 100ms, Integration tests < 1s.
7. The Chain Gang - Test Dependency
Problem: Tests depend on other tests running first.
// BAD - Tests must run in orderdescribe('User operations', () => {
let userId;
it('should create user', () => {
userId = createUser(); // Sets state for next testexpect(userId).toBeDefined();
});
it('should update user', () => {
updateUser(userId, newData); // Depends on previous testexpect(getUser(userId).name).toBe(newData.name);
});
});
// GOOD - Each test is independentdescribe('User operations', () => {
it('should create user', () => {
const userId = createUser();
expect(userId).toBeDefined();
});
it('should update user', () => {
const userId = createUser(); // Creates its own userupdateUser(userId, newData);
expect(getUser(userId).name).toBe(newData.name);
});
});
8. The Secret Catcher - Hidden Test Logic
Problem: Test logic is hidden in helpers or setup.
// BAD - Assertions hidden in helperfunctionassertValidUser(user) {
expect(user.id).toBeDefined();
expect(user.email).toMatch(/@/);
expect(user.createdAt).toBeInstanceOf(Date);
// Many more hidden assertions
}
it('should create valid user', () => {
const user = createUser(data);
assertValidUser(user); // What is actually being tested?
});
// GOOD - Explicit assertionsit('should create user with email', () => {
const user = createUser(data);
expect(user.email).toBe(data.email);
});