Настройка Jest
Jest — самый популярный фреймворк для тестирования JavaScript. Работает «из коробки» с минимальной настройкой.
Зачем нужно
Jest объединяет test runner, assertion library и mocking в одном пакете. Не нужно собирать по частям (Mocha + Chai + Sinon). Поддерживает snapshot-тесты, параллельное выполнение, coverage — всё включено.
Где используется
React-проекты (create-react-app включает Jest по умолчанию), Node.js-сервисы, npm-пакеты, любые JavaScript/TypeScript проекты.
Предпосылки
Основы тестирования, Node.js и npm установлены
Установка
Базовая установка
# npm
npm install --save-dev jest
# yarn
yarn add --dev jest
# pnpm
pnpm add -D jest
Для TypeScript
npm install --save-dev jest ts-jest @types/jest
Для ES Modules (import/export)
npm install --save-dev jest @babel/core @babel/preset-env babel-jest
Настройка package.json
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"test:verbose": "jest --verbose",
"test:ci": "jest --ci --coverage --maxWorkers=2"
}
}
jest.config.js
Минимальная конфигурация
// jest.config.js
module.exports = {
// Где искать тесты
testMatch: [
'**/__tests__/**/*.js',
'**/*.test.js',
'**/*.spec.js',
],
// Какие файлы считать тестовыми
testPathIgnorePatterns: ['/node_modules/', '/dist/'],
};
Полная конфигурация
// jest.config.js
module.exports = {
// === Поиск тестов ===
roots: ['<rootDir>/src'],
testMatch: ['**/__tests__/**/*.{js,ts}', '**/*.{test,spec}.{js,ts}'],
testPathIgnorePatterns: ['/node_modules/', '/dist/', '/build/'],
// === Трансформация ===
transform: {
'^.+\\.tsx?$': 'ts-jest', // TypeScript
'^.+\\.jsx?$': 'babel-jest', // JSX/JS
},
// === Модули ===
moduleNameMapper: {
// Алиасы путей (как в webpack/tsconfig)
'^@/(.*)$': '<rootDir>/src/$1',
'^@components/(.*)$': '<rootDir>/src/components/$1',
'^@utils/(.*)$': '<rootDir>/src/utils/$1',
// Заглушки для не-JS файлов
'\\.(css|less|scss)$': 'identity-obj-proxy',
'\\.(jpg|jpeg|png|gif|svg)$': '<rootDir>/__mocks__/fileMock.js',
},
moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json'],
// === Окружение ===
testEnvironment: 'node', // 'jsdom' для браузерного кода
// === Setup ===
setupFiles: ['<rootDir>/jest.setup.js'], // До каждого теста
setupFilesAfterFramework: ,
globalSetup: '<rootDir>/jest.globalSetup.js', // Один раз перед всеми
globalTeardown: '<rootDir>/jest.globalTeardown.js', // Один раз после всех
// === Coverage ===
collectCoverageFrom: [
'src/**/*.{js,ts}',
'!src/**/*.d.ts',
'!src/index.ts',
'!src/**/__tests__/**',
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
coverageReporters: ['text', 'lcov', 'clover'],
// === Производительность ===
maxWorkers: '50%', // Количество потоков
verbose: true, // Подробный вывод
};
Конфигурация для TypeScript (ts-jest)
// jest.config.ts
import type { Config } from 'jest';
const config: Config = {
preset: 'ts-jest',
testEnvironment: 'node',
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
};
export default config;
babel.config.js (для ES Modules)
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }],
],
};
Структура файлов
src/
├── utils/
│ ├── math.js
│ ├── math.test.js ← Тест рядом с файлом
│ ├── string.js
│ └── __tests__/
│ └── string.test.js ← Тесты в папке __tests__
├── services/
│ ├── userService.js
│ └── userService.spec.js ← .spec.js тоже работает
└── __mocks__/
├── axios.js ← Ручной мок модуля
└── fileMock.js ← Заглушка для файлов
fileMock.js
// __mocks__/fileMock.js
module.exports = 'test-file-stub';
Режимы запуска
Watch mode — перезапуск при изменениях
jest --watch # Только изменённые файлы (нужен git)
jest --watchAll # Все тесты при любом изменении
Горячие клавиши в watch mode:
p— фильтр по имени файлаt— фильтр по имени тестаf— запустить только упавшиеa— запустить всеq— выйти
Coverage — покрытие кода
jest --coverage
Результат:
----------|---------|----------|---------|---------|
File | % Stmts | % Branch | % Funcs | % Lines |
----------|---------|----------|---------|---------|
math.js | 100 | 87.5 | 100 | 100 |
string.js | 75 | 50 | 66.7 | 75 |
----------|---------|----------|---------|---------|
Запуск конкретных тестов
jest math # Файлы, содержащие "math"
jest --testPathPattern=utils # Файлы в путях с "utils"
jest -t "сложение" # Тесты с именем "сложение"
jest path/to/file.test.js # Конкретный файл
Setup и Teardown
// jest.setup.js — выполняется перед каждым тестовым файлом
require('dotenv').config({ path: '.env.test' });
// Глобальные моки
jest.setTimeout(10000); // Таймаут для всех тестов
// В тестовом файле
beforeAll(() => {
// Один раз перед всеми тестами в файле
// Подключение к тестовой БД
});
afterAll(() => {
// Один раз после всех тестов
// Закрытие соединения
});
beforeEach(() => {
// Перед каждым тестом
// Очистка данных
});
afterEach(() => {
// После каждого теста
jest.clearAllMocks;
});
Частые ошибки
1. Забыл transform для TypeScript
SyntaxError: Cannot use import statement outside a module
Решение: добавить ts-jest или babel-jest в transform.
2. CSS/изображения ломают тесты
SyntaxError: Unexpected token '.' (при import './styles.css')
Решение: moduleNameMapper для CSS и файлов.
3. Алиасы путей не работают
Cannot find module '@/utils/math'
Решение: moduleNameMapper должен совпадать с tsconfig.json paths.
4. Тесты медленные
// Проверь:
// 1. maxWorkers: '50%' — параллелизм
// 2. Нет реальных HTTP/DB вызовов в unit-тестах
// 3. jest --bail — остановка при первом фейле
Практика
- Создай новый проект с Jest:
npm init -y && npm i -D jest - Настрой
jest.config.jsсroots,testMatch,verbose - Добавь скрипты
test,test:watch,test:coverageвpackage.json - Создай mock для CSS-файлов через
moduleNameMapper - Настрой
coverageThresholdна 80% и убедись, что тесты не проходят при недостаточном покрытии