| name | jest |
| description | Execute and generate Jest tests for JavaScript/TypeScript projects |
| allowed-tools | Read, Bash, Grep, Glob |
Jest Testing Skill
Execute and generate Jest tests for JavaScript/TypeScript projects with support for unit, integration, and E2E testing.
Test Execution
Run All Tests
npm test
npx jest
Run Specific Tests
npx jest --testNamePattern="should create user"
npx jest src/services/user.test.ts
npx jest --watch
npx jest --coverage
Common Options
npx jest --verbose
npx jest --onlyChanged
npx jest --updateSnapshot
npx jest --ci --coverage --reporters=default --reporters=jest-junit
Test Patterns
Basic Test Structure
describe('UserService', () => {
let service: UserService;
let mockRepo: jest.Mocked<UserRepository>;
beforeEach(() => {
mockRepo = {
findById: jest.fn(),
create: jest.fn(),
update: jest.fn(),
delete: jest.fn(),
};
service = new UserService(mockRepo);
});
afterEach(() => {
jest.clearAllMocks();
});
describe('getUser', () => {
it('should return user when found', async () => {
const user = { id: '1', name: 'John' };
mockRepo.findById.mockResolvedValue(user);
const result = await service.getUser('1');
expect(result).toEqual(user);
expect(mockRepo.findById).toHaveBeenCalledWith('1');
});
it('should throw when user not found', async () => {
mockRepo.findById.mockResolvedValue(null);
await expect(service.getUser('999')).rejects.toThrow('User not found');
});
});
});
Mocking
jest.mock('./api', () => ({
fetchUser: jest.fn(),
}));
import { fetchUser } from './api';
const mockFetchUser = fetchUser as jest.MockedFunction<typeof fetchUser>;
beforeEach(() => {
mockFetchUser.mockResolvedValue({ id: '1', name: 'Test' });
});
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
jest.useFakeTimers();
jest.advanceTimersByTime(1000);
jest.useRealTimers();
Async Testing
it('fetches data asynchronously', async () => {
const data = await fetchData();
expect(data).toEqual({ id: 1 });
});
it('handles errors', async () => {
await expect(fetchData()).rejects.toThrow('Network error');
});
it('updates state after fetch', async () => {
render(<Component />);
await waitFor(() => {
expect(screen.getByText('Loaded')).toBeInTheDocument();
});
});
React Component Testing
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
describe('LoginForm', () => {
it('submits with valid credentials', async () => {
const onSubmit = jest.fn();
render(<LoginForm onSubmit={onSubmit} />);
await userEvent.type(screen.getByLabelText('Email'), 'test@example.com');
await userEvent.type(screen.getByLabelText('Password'), 'password123');
await userEvent.click(screen.getByRole('button', { name: /submit/i }));
expect(onSubmit).toHaveBeenCalledWith({
email: 'test@example.com',
password: 'password123',
});
});
it('shows validation errors', async () => {
render(<LoginForm onSubmit={jest.fn()} />);
await userEvent.click(screen.getByRole('button', { name: /submit/i }));
expect(screen.getByText('Email is required')).toBeInTheDocument();
});
});
Configuration
jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: ['**/*.test.ts', '**/*.spec.ts'],
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
'!src/**/*.test.ts',
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
setupFilesAfterEnv: ['<rootDir>/jest.setup.ts'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
};
jest.setup.ts
import '@testing-library/jest-dom';
global.fetch = jest.fn();
expect.extend({
toBeWithinRange(received: number, floor: number, ceiling: number) {
const pass = received >= floor && received <= ceiling;
return {
message: () =>
`expected ${received} ${pass ? 'not ' : ''}to be within range ${floor} - ${ceiling}`,
pass,
};
},
});
Troubleshooting
Common Issues
- Tests hanging: Check for unresolved promises or missing
done() callbacks
- Mock not working: Ensure mock is defined before importing the module
- Timeout errors: Increase timeout with
jest.setTimeout(30000)
- Memory issues: Run with
--maxWorkers=2 or --runInBand