Jest 高级配置 #

配置文件详解 #

配置文件优先级 #

Jest 按以下顺序查找配置文件:

text
1. --config <path> 命令行参数
2. package.json 中的 "jest" 字段
3. jest.config.js
4. jest.config.ts
5. jest.config.json
6. jest.config.mjs

完整配置示例 #

javascript
// jest.config.js
module.exports = {
  // 基本配置
  rootDir: './src',
  testEnvironment: 'jsdom',
  
  // 测试匹配
  testMatch: [
    '**/__tests__/**/*.[jt]s?(x)',
    '**/?(*.)+(spec|test).[jt]s?(x)',
  ],
  testPathIgnorePatterns: [
    '/node_modules/',
    '/dist/',
  ],
  
  // 模块配置
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/$1',
    '\\.(css|less|scss|sass)$': 'identity-obj-proxy',
  },
  moduleFileExtensions: ['js', 'json', 'jsx', 'ts', 'tsx'],
  
  // 转换配置
  transform: {
    '^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['@babel/preset-env'] }],
  },
  transformIgnorePatterns: [
    '/node_modules/',
  ],
  
  // 设置文件
  setupFiles: ['<rootDir>/jest.env.js'],
  setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
  
  // 覆盖率配置
  collectCoverage: true,
  coverageDirectory: 'coverage',
  collectCoverageFrom: [
    '**/*.{js,jsx,ts,tsx}',
    '!**/*.d.ts',
    '!**/node_modules/**',
  ],
  coverageThreshold: {
    global: {
      branches: 80,
      functions: 80,
      lines: 80,
    },
  },
  
  // 性能配置
  maxWorkers: '50%',
  cacheDirectory: '/tmp/jest_cache',
  
  // 其他配置
  verbose: true,
  testTimeout: 5000,
  detectOpenHandles: true,
};

预设配置 #

使用预设 #

javascript
// 使用 ts-jest 预设
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'jsdom',
};

// 使用 React 预设
module.exports = {
  preset: 'react-scripts',
};

// 使用 Vue 预设
module.exports = {
  preset: '@vue/cli-plugin-unit-jest',
};

自定义预设 #

javascript
// my-jest-preset/index.js
module.exports = {
  testEnvironment: 'jsdom',
  setupFilesAfterEnv: ['./setup.js'],
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
  },
};

// 使用自定义预设
module.exports = {
  preset: 'my-jest-preset',
};

全局设置 #

globalSetup #

在所有测试之前执行一次:

javascript
// globalSetup.js
module.exports = async () => {
  // 启动测试数据库
  await startTestDatabase();
  
  // 设置全局变量
  global.__TEST_DB__ = testDb;
  
  console.log('Global setup complete');
};

// jest.config.js
module.exports = {
  globalSetup: './globalSetup.js',
};

globalTeardown #

在所有测试之后执行一次:

javascript
// globalTeardown.js
module.exports = async () => {
  // 关闭测试数据库
  await global.__TEST_DB__.close();
  
  console.log('Global teardown complete');
};

// jest.config.js
module.exports = {
  globalTeardown: './globalTeardown.js',
};

组合使用 #

javascript
// jest.config.js
module.exports = {
  globalSetup: './globalSetup.js',
  globalTeardown: './globalTeardown.js',
};

// globalSetup.js
module.exports = async () => {
  const server = await startServer();
  return async () => {
    await server.close();
  };
};

测试环境 #

自定义测试环境 #

javascript
// customEnvironment.js
const TestEnvironment = require('jest-environment-node');

class CustomEnvironment extends TestEnvironment {
  constructor(config, context) {
    super(config, context);
    this.testPath = context.testPath;
  }

  async setup() {
    await super.setup();
    // 自定义设置
    this.global.myCustomGlobal = 'test value';
  }

  async teardown() {
    // 自定义清理
    await super.teardown();
  }

  getVmContext() {
    return super.getVmContext();
  }
}

module.exports = CustomEnvironment;

// jest.config.js
module.exports = {
  testEnvironment: './customEnvironment.js',
};

jsdom 环境配置 #

javascript
module.exports = {
  testEnvironment: 'jsdom',
  testEnvironmentOptions: {
    html: '<html><body><div id="root"></div></body></html>',
    url: 'https://example.com',
    userAgent: 'Mozilla/5.0',
    runScripts: 'dangerously',
    resources: 'usable',
  },
};

多项目配置 #

项目配置 #

javascript
// jest.config.js
module.exports = {
  projects: [
    {
      displayName: 'client',
      testEnvironment: 'jsdom',
      testMatch: ['<rootDir>/client/**/*.test.js'],
    },
    {
      displayName: 'server',
      testEnvironment: 'node',
      testMatch: ['<rootDir>/server/**/*.test.js'],
    },
    {
      displayName: 'shared',
      testEnvironment: 'node',
      testMatch: ['<rootDir>/shared/**/*.test.js'],
    },
  ],
};

运行特定项目 #

bash
# 运行所有项目
jest

# 运行特定项目
jest --selectProjects client server

# 忽略特定项目
jest --ignoreProjects shared

自定义匹配器 #

创建自定义匹配器 #

javascript
// matchers.js
expect.extend({
  toBeWithinRange(received, floor, ceiling) {
    const pass = received >= floor && received <= ceiling;
    if (pass) {
      return {
        message: () => `expected ${received} not to be within range ${floor} - ${ceiling}`,
        pass: true,
      };
    } else {
      return {
        message: () => `expected ${received} to be within range ${floor} - ${ceiling}`,
        pass: false,
      };
    }
  },

  toBeValidEmail(received) {
    const pass = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(received);
    return {
      pass,
      message: () => pass
        ? `expected ${received} not to be a valid email`
        : `expected ${received} to be a valid email`,
    };
  },
});

// jest.setup.js
import './matchers';

// 使用
test('custom matchers', () => {
  expect(5).toBeWithinRange(1, 10);
  expect('test@example.com').toBeValidEmail();
});

TypeScript 支持 #

typescript
// matchers.d.ts
declare global {
  namespace jest {
    interface Matchers<R> {
      toBeWithinRange(floor: number, ceiling: number): R;
      toBeValidEmail(): R;
    }
  }
}

export {};

模块路径映射 #

复杂映射 #

javascript
module.exports = {
  moduleNameMapper: {
    // 简单映射
    '^@/(.*)$': '<rootDir>/src/$1',
    
    // 多个目录映射
    '^@components/(.*)$': '<rootDir>/src/components/$1',
    '^@utils/(.*)$': '<rootDir>/src/utils/$1',
    '^@services/(.*)$': '<rootDir>/src/services/$1',
    
    // 静态资源映射
    '\\.(css|less|scss|sass)$': 'identity-obj-proxy',
    '\\.(jpg|jpeg|png|gif|svg)$': '<rootDir>/__mocks__/fileMock.js',
    
    // 条件映射
    '^lodash$': 'lodash-es',
  },
};

转换配置 #

多种文件类型转换 #

javascript
module.exports = {
  transform: {
    // JavaScript/TypeScript
    '^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
    
    // Vue 组件
    '^.+\\.vue$': '@vue/vue3-jest',
    
    // CSS Modules
    '^.+\\.module\\.(css|sass|scss)$': 'jest-css-modules-transform',
    
    // GraphQL
    '\\.(gql|graphql)$': 'jest-transform-graphql',
  },
};

忽略转换 #

javascript
module.exports = {
  transformIgnorePatterns: [
    '/node_modules/(?!(@myorg|lodash-es)/)',
  ],
};

测试隔离 #

每个测试独立进程 #

javascript
module.exports = {
  // 每个测试文件在独立进程中运行
  runner: 'jest-runner',
  
  // 每个测试独立 VM
  testRunner: 'jest-circus/runner',
};

快照配置 #

javascript
module.exports = {
  // 快照序列化器
  snapshotSerializers: [
    'enzyme-to-json/serializer',
    'my-custom-serializer',
  ],
  
  // 快照格式
  snapshotFormat: {
    escapeString: true,
    printBasicPrototype: true,
  },
};

监视模式配置 #

javascript
module.exports = {
  // 监视模式插件
  watchPlugins: [
    'jest-watch-typeahead/filename',
    'jest-watch-typeahead/testname',
    'jest-watch-select-projects',
  ],
  
  // 监视忽略模式
  watchPathIgnorePatterns: [
    '/node_modules/',
    '/dist/',
  ],
};

报告器配置 #

自定义报告器 #

javascript
// customReporter.js
class CustomReporter {
  constructor(globalConfig, options) {
    this._globalConfig = globalConfig;
    this._options = options;
  }

  onRunComplete(contexts, results) {
    console.log('Custom reporter output:');
    console.log(`Total tests: ${results.numTotalTests}`);
    console.log(`Passed: ${results.numPassedTests}`);
    console.log(`Failed: ${results.numFailedTests}`);
  }
}

module.exports = CustomReporter;

// jest.config.js
module.exports = {
  reporters: [
    'default',
    ['./customReporter.js', { option: 'value' }],
  ],
};

性能优化配置 #

javascript
module.exports = {
  // 最大工作进程数
  maxWorkers: '50%',
  
  // 缓存目录
  cacheDirectory: '/tmp/jest_cache',
  
  // 启用缓存
  cache: true,
  
  // 快速失败
  bail: 1,
  
  // 测试超时
  testTimeout: 5000,
  
  // 并行执行
  maxConcurrency: 5,
};

最佳实践 #

1. 分离配置 #

javascript
// jest.config.base.js
module.exports = {
  testEnvironment: 'jsdom',
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1',
  },
};

// jest.config.js
const baseConfig = require('./jest.config.base');

module.exports = {
  ...baseConfig,
  // 特定配置
};

2. 环境变量 #

javascript
// jest.config.js
module.exports = {
  testEnvironment: process.env.TEST_ENV || 'jsdom',
  maxWorkers: process.env.CI ? 2 : '50%',
};

3. 条件配置 #

javascript
// jest.config.js
const isCI = process.env.CI === 'true';

module.exports = {
  cache: !isCI,
  coverage: isCI,
  maxWorkers: isCI ? 2 : '50%',
};

下一步 #

现在你已经掌握了 Jest 高级配置,接下来学习 性能优化 提升测试效率!

最后更新:2026-03-28