Jest Core Knowledge
Deep Knowledge: Use mcp__documentation__fetch_docs with technology: jest for comprehensive documentation.
When NOT to Use This Skill
-
E2E Testing - Use playwright or cypress for browser automation
-
Vite Projects - Use vitest which is faster and Vite-native
-
Component Testing Alone - Combine with testing-library for React/Vue components
-
API Integration Tests - Use framework-specific skills (spring-boot-integration, etc.)
Basic Test
describe('UserService', () => { let service: UserService;
beforeEach(() => { service = new UserService(); });
afterEach(() => { jest.clearAllMocks(); });
it('should create user', async () => { const user = await service.create({ name: 'John' }); expect(user.name).toBe('John'); }); });
Mocking
// Mock function const mockFn = jest.fn(); mockFn.mockReturnValue(42); mockFn.mockResolvedValue({ data: [] });
// Mock module jest.mock('./api', () => ({ fetchUsers: jest.fn().mockResolvedValue([]) }));
// Spy const spy = jest.spyOn(console, 'log'); expect(spy).toHaveBeenCalledWith('message');
// Mock implementation mockFn.mockImplementation((x) => x * 2);
Matchers
// Same as Vitest, Jest-compatible expect(value).toBe(exact); expect(value).toEqual(deepEqual); expect(obj).toMatchObject({ name: 'John' }); expect(arr).toContainEqual({ id: 1 });
Snapshots
it('should match snapshot', () => { const component = render(<UserCard user={user} />); expect(component).toMatchSnapshot(); });
it('should match inline snapshot', () => {
expect(formatDate(date)).toMatchInlineSnapshot("2024-01-15");
});
Async Testing
it('should resolve', async () => { await expect(fetchData()).resolves.toEqual({ data: [] }); });
it('should reject', async () => { await expect(fetchFail()).rejects.toThrow('Error'); });
Config
// jest.config.js module.exports = { testEnvironment: 'jsdom', setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'], moduleNameMapper: { '^@/(.*)$': '<rootDir>/src/$1' } };
Production Readiness
Test Organization
// Isolated tests with proper setup/teardown describe('OrderService', () => { let service: OrderService; let mockRepository: jest.Mocked<OrderRepository>;
beforeEach(() => { mockRepository = { findById: jest.fn(), save: jest.fn(), delete: jest.fn(), } as jest.Mocked<OrderRepository>;
service = new OrderService(mockRepository);
});
afterEach(() => { jest.clearAllMocks(); });
describe('processOrder', () => { it('should process valid order', async () => { // Arrange const order = createTestOrder({ status: 'pending' }); mockRepository.findById.mockResolvedValue(order); mockRepository.save.mockResolvedValue({ ...order, status: 'processed' });
// Act
const result = await service.processOrder(order.id);
// Assert
expect(result.status).toBe('processed');
expect(mockRepository.save).toHaveBeenCalledWith(
expect.objectContaining({ status: 'processed' })
);
});
}); });
Coverage Configuration
// jest.config.js module.exports = { collectCoverageFrom: [ 'src//*.{ts,tsx}', '!src//.d.ts', '!src//index.ts', '!src//.stories.tsx', ], coverageThreshold: { global: { branches: 75, functions: 80, lines: 80, statements: 80, }, }, coverageReporters: ['text', 'lcov', 'html'], };
CI Integration
GitHub Actions
-
name: Run tests run: npm test -- --ci --coverage --reporters=default --reporters=jest-junit env: JEST_JUNIT_OUTPUT_DIR: ./reports
-
name: Upload coverage uses: codecov/codecov-action@v3
// package.json { "scripts": { "test": "jest", "test:ci": "jest --ci --coverage --maxWorkers=2", "test:watch": "jest --watch" } }
Performance Optimization
// jest.config.js - Speed up tests module.exports = { // Run tests in parallel maxWorkers: '50%',
// Cache transformations cacheDirectory: '<rootDir>/.jest-cache',
// Only run affected tests onlyChanged: true,
// Fail fast in CI bail: process.env.CI ? 1 : 0, };
Monitoring Metrics
Metric Target
Line coverage
80%
Branch coverage
75%
Test execution time < 120s
Flaky test rate 0%
Checklist
-
Arrange-Act-Assert pattern
-
Isolated tests (no shared state)
-
Coverage thresholds enforced
-
CI/CD integration with reporting
-
jest.clearAllMocks() in afterEach
-
Meaningful test descriptions
-
Async tests properly awaited
-
Snapshot tests reviewed
-
Test data factories
-
Error scenarios covered
Anti-Patterns
Anti-Pattern Why It's Bad Solution
Over-using snapshots Tests pass without validation Use snapshots sparingly, prefer explicit assertions
Not reviewing snapshot changes Bugs slip into snapshots Always review snapshot diffs manually
Testing implementation details Brittle tests Test user-facing behavior
Shared mutable state Flaky tests Isolate setup with beforeEach
Skipping error paths Production failures Test both success and error scenarios
Large snapshot files Hard to review Break into smaller focused snapshots
No mock cleanup Tests affect each other jest.clearAllMocks() in afterEach
Quick Troubleshooting
Problem Likely Cause Solution
"Cannot find module" Mock path incorrect Ensure mock path matches import exactly
Test timeout Missing await Add await to all async operations
"Received: serializes to same string" Object reference comparison Use toEqual() not toBe() for objects
Snapshot mismatch Intentional code change Review diff, update with -u flag if correct
"expect().resolves is not a function" Old Jest version Update to Jest 27+ or use async/await
Tests pass locally, fail in CI Timezone/locale differences Mock Date, use fixed timezones
Reference Documentation
-
Mocking
-
Snapshots