Jest 高级配置 #
配置文件详解 #
配置文件优先级 #
Jest 按以下顺序查找配置文件:
text
1. --config <path> 命令行指定
2. jest.config.ts TypeScript 配置
3. jest.config.js JavaScript 配置
4. jest.config.json JSON 配置
5. package.json "jest" package.json 中配置
完整配置示例 #
javascript
// jest.config.js
module.exports = {
// 基本配置
rootDir: './',
displayName: 'my-project',
// 测试环境
testEnvironment: 'jsdom',
testEnvironmentOptions: {
html: '<!DOCTYPE html>',
url: 'http://localhost',
},
// 测试匹配
testMatch: [
'**/__tests__/**/*.[jt]s?(x)',
'**/?(*.)+(spec|test).[jt]s?(x)',
],
testRegex: '(/__tests__/.*|(\\.|/)(test|spec))\\.[jt]sx?$',
testPathIgnorePatterns: [
'/node_modules/',
'/dist/',
],
// 模块配置
moduleFileExtensions: ['js', 'jsx', 'ts', 'tsx', 'json'],
moduleDirectories: ['node_modules', 'src'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
},
modulePathIgnorePatterns: ['<rootDir>/dist/'],
// 转换配置
transform: {
'^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
},
transformIgnorePatterns: [
'node_modules/(?!(module-to-transform)/)',
],
// 设置文件
setupFiles: ['<rootDir>/jest.env.js'],
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
globalSetup: '<rootDir>/jest.globalSetup.js',
globalTeardown: '<rootDir>/jest.globalTeardown.js',
// 覆盖率
collectCoverage: false,
coverageDirectory: 'coverage',
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.d.ts',
],
coverageReporters: ['text', 'lcov', 'html'],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
},
},
coveragePathIgnorePatterns: [
'/node_modules/',
'/tests/',
],
// 执行配置
maxWorkers: '50%',
testTimeout: 5000,
slowTestThreshold: 300,
// 输出配置
verbose: true,
silent: false,
notify: true,
notifyMode: 'failure-change',
// 快照配置
snapshotSerializers: ['jest-emotion'],
snapshotFormat: {
escapeString: true,
printBasicPrototype: true,
},
// 缓存
cache: true,
cacheDirectory: '/tmp/jest_cache',
clearMocks: true,
resetMocks: false,
restoreMocks: false,
// 监听配置
watch: false,
watchAll: false,
watchPlugins: [
'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname',
],
watchPathIgnorePatterns: [
'<rootDir>/dist/',
],
// 其他
errorOnDeprecated: true,
forceCoverageMatch: ['**/*.test.{js,ts}'],
injectGlobals: true,
passWithNoTests: false,
projects: null,
runner: 'jest-runner',
testRunner: 'jest-circus/runner',
};
预设 (Presets) #
使用预设 #
javascript
// jest.config.js
module.exports = {
preset: 'ts-jest', // 使用 ts-jest 预设
};
常用预设 #
| 预设 | 用途 |
|---|---|
ts-jest |
TypeScript 项目 |
@vue/vue3-jest |
Vue 3 项目 |
react-scripts |
Create React App |
jest-expo |
React Native |
@shelf/jest-mongodb |
MongoDB 测试 |
自定义预设 #
javascript
// my-jest-preset.js
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
},
};
// jest.config.js
module.exports = {
preset: './my-jest-preset.js',
};
全局设置 #
globalSetup / globalTeardown #
javascript
// jest.globalSetup.js
module.exports = async () => {
// 启动数据库
global.__DB__ = await startDatabase();
console.log('Global setup complete');
};
// jest.globalTeardown.js
module.exports = async () => {
// 关闭数据库
await global.__DB__.close();
console.log('Global teardown complete');
};
// jest.config.js
module.exports = {
globalSetup: './jest.globalSetup.js',
globalTeardown: './jest.globalTeardown.js',
};
setupFiles #
在测试环境设置之前执行:
javascript
// jest.env.js
process.env.NODE_ENV = 'test';
process.env.API_URL = 'http://test-api.example.com';
// 设置全局变量
global.fetch = require('node-fetch');
// jest.config.js
module.exports = {
setupFiles: ['<rootDir>/jest.env.js'],
};
setupFilesAfterEnv #
在测试环境设置之后执行:
javascript
// jest.setup.js
import '@testing-library/jest-dom';
// 扩展 Jest 匹配器
expect.extend({
toBeWithinRange(received, floor, ceiling) {
const pass = received >= floor && received <= ceiling;
return {
pass,
message: () => `expected ${received} ${pass ? 'not ' : ''}to be within range ${floor}-${ceiling}`,
};
},
});
// 全局设置
beforeAll(() => {
console.log('Tests starting...');
});
afterAll(() => {
console.log('Tests finished.');
});
// jest.config.js
module.exports = {
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
};
自定义匹配器 #
创建自定义匹配器 #
javascript
// jest.setup.js
expect.extend({
toBeEven(received) {
const pass = received % 2 === 0;
return {
pass,
message: () => `expected ${received} ${pass ? 'not ' : ''}to be even`,
};
},
toBeWithinRange(received, floor, ceiling) {
const pass = received >= floor && received <= ceiling;
return {
pass,
message: () => `expected ${received} ${pass ? 'not ' : ''}to be within range ${floor}-${ceiling}`,
};
},
toBeValidEmail(received) {
const pass = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(received);
return {
pass,
message: () => `expected ${received} ${pass ? 'not ' : ''}to be a valid email`,
};
},
});
// 使用
test('custom matchers', () => {
expect(4).toBeEven();
expect(5).toBeWithinRange(1, 10);
expect('test@example.com').toBeValidEmail();
});
TypeScript 支持 #
typescript
// jest.d.ts
declare global {
namespace jest {
interface Matchers<R> {
toBeEven(): R;
toBeWithinRange(floor: number, ceiling: number): R;
toBeValidEmail(): R;
}
}
}
export {};
测试环境 #
自定义测试环境 #
javascript
// custom-environment.js
const { TestEnvironment } = require('jest-environment-node');
class CustomEnvironment extends TestEnvironment {
constructor(config, context) {
super(config, context);
console.log('Custom environment setup');
}
async setup() {
await super.setup();
this.global.customGlobal = 'value';
}
async teardown() {
console.log('Custom environment teardown');
await super.teardown();
}
getVmContext() {
return super.getVmContext();
}
}
module.exports = CustomEnvironment;
// jest.config.js
module.exports = {
testEnvironment: './custom-environment.js',
};
jsdom 环境选项 #
javascript
module.exports = {
testEnvironment: 'jsdom',
testEnvironmentOptions: {
html: '<html><body><div id="root"></div></body></html>',
url: 'http://localhost:3000',
userAgent: 'Mozilla/5.0',
runScripts: 'dangerously',
resources: 'usable',
pretendToBeVisual: true,
},
};
多项目配置 #
projects 配置 #
javascript
// jest.config.js
module.exports = {
projects: [
{
displayName: 'dom',
testEnvironment: 'jsdom',
testMatch: ['**/*.dom.test.js'],
},
{
displayName: 'node',
testEnvironment: 'node',
testMatch: ['**/*.node.test.js'],
},
{
displayName: 'e2e',
testEnvironment: 'node',
testMatch: ['**/*.e2e.test.js'],
testTimeout: 30000,
},
],
};
运行特定项目 #
bash
jest --selectProjects dom
jest --selectProjects node e2e
自定义运行器 #
创建自定义运行器 #
javascript
// custom-runner.js
const { TestRunner } = require('jest-runner');
class CustomRunner extends TestRunner {
async runTests(tests, watcher, onStart, onResult, onFailure, options) {
console.log('Running tests with custom runner');
return super.runTests(tests, watcher, onStart, onResult, onFailure, options);
}
}
module.exports = CustomRunner;
// jest.config.js
module.exports = {
runner: './custom-runner.js',
};
监听插件 #
配置监听插件 #
javascript
module.exports = {
watchPlugins: [
'jest-watch-typeahead/filename',
'jest-watch-typeahead/testname',
[
'jest-watch-toggle-config',
{ setting: 'verbose' },
],
[
'jest-watch-toggle-config',
{ setting: 'collectCoverage' },
],
],
};
自定义监听插件 #
javascript
// custom-watch-plugin.js
class CustomWatchPlugin {
constructor({ stdin, stdout, config }) {
this._stdin = stdin;
this._stdout = stdout;
this._config = config;
}
apply(jestHooks) {
jestHooks.onFileChange(({ projects }) => {
console.log('Files changed');
});
}
getUsageInfo() {
return {
key: 'c',
prompt: 'run custom command',
};
}
run(globalConfig, updateConfigAndRun) {
console.log('Running custom command');
return Promise.resolve(true);
}
}
module.exports = CustomWatchPlugin;
环境变量 #
在配置中使用环境变量 #
javascript
// jest.config.js
module.exports = {
testEnvironment: process.env.TEST_ENV || 'jsdom',
maxWorkers: process.env.CI ? 2 : '50%',
testTimeout: process.env.CI ? 10000 : 5000,
};
.env 文件支持 #
javascript
// jest.env.js
require('dotenv').config({ path: '.env.test' });
// jest.config.js
module.exports = {
setupFiles: ['<rootDir>/jest.env.js'],
};
快照配置 #
快照格式化 #
javascript
module.exports = {
snapshotFormat: {
escapeString: true,
printBasicPrototype: true,
highlight: true,
indent: 2,
maxDepth: 10,
min: false,
plugins: [],
},
};
快照序列化器 #
javascript
// custom-serializer.js
module.exports = {
test: (val) => val && val.isCustomType,
serialize: (val, config, indentation, depth, refs, printer) => {
return `Custom(${val.name})`;
},
};
// jest.config.js
module.exports = {
snapshotSerializers: ['./custom-serializer.js'],
};
性能优化配置 #
并行执行 #
javascript
module.exports = {
maxWorkers: '50%', // 使用 50% CPU 核心
// 或
maxWorkers: 4, // 使用 4 个 worker
};
缓存配置 #
javascript
module.exports = {
cache: true,
cacheDirectory: '/tmp/jest_cache',
};
跳过转换 #
javascript
module.exports = {
transformIgnorePatterns: [
'node_modules/(?!(react-router-dom|axios)/)',
],
};
常用配置模板 #
React 项目 #
javascript
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1',
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
'\\.(jpg|jpeg|png|gif|svg)$': '<rootDir>/__mocks__/fileMock.js',
},
transform: {
'^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
},
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.d.ts',
'!src/index.js',
],
};
Node.js 项目 #
javascript
module.exports = {
testEnvironment: 'node',
testMatch: ['**/*.test.js'],
collectCoverageFrom: [
'src/**/*.js',
'!src/**/*.test.js',
],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
},
},
};
TypeScript 项目 #
javascript
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src'],
testMatch: ['**/*.test.ts'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
};
下一步 #
现在你已经掌握了 Jest 高级配置,接下来学习 性能优化 提升测试效率!
最后更新:2026-03-28