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