Enzyme 安装与配置 #
安装 Enzyme #
基本安装 #
Enzyme 需要配合测试框架(如 Jest)一起使用:
bash
# 安装 Enzyme 核心包
npm install --save-dev enzyme
# 安装 React 适配器(根据 React 版本选择)
# React 16
npm install --save-dev enzyme-adapter-react-16
# React 16.8+(支持 Hooks)
npm install --save-dev enzyme-adapter-react-16.3
# React 17(非官方适配器)
npm install --save-dev @wojtekmaj/enzyme-adapter-react-17
# React 18(非官方适配器)
npm install --save-dev @cfaester/enzyme-adapter-react-18
完整安装示例 #
bash
# 创建新项目
npx create-react-app my-app
cd my-app
# 安装 Enzyme 和适配器
npm install --save-dev enzyme @wojtekmaj/enzyme-adapter-react-17
# 安装 Jest(如果尚未安装)
npm install --save-dev jest
适配器配置 #
为什么需要适配器? #
text
┌─────────────────────────────────────────────────────────────┐
│ Enzyme 架构 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Enzyme 核心 │ │
│ │ • 统一的测试 API │ │
│ │ • 选择器引擎 │ │
│ │ • 遍历和操作方法 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↕ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ 适配器层 │ │
│ │ • 适配不同版本的 React │ │
│ │ • 处理版本差异 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↕ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ React 版本 │ │
│ │ React 16 / React 17 / React 18 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
配置方式 #
方式一:在测试文件中配置 #
javascript
// setupTests.js
import Enzyme from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
Enzyme.configure({ adapter: new Adapter() });
方式二:在 Jest 配置中引入 #
javascript
// jest.config.js
module.exports = {
setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
};
方式三:在 package.json 中配置 #
json
{
"jest": {
"setupFilesAfterEnv": ["<rootDir>/src/setupTests.js"]
}
}
完整配置示例 #
项目结构 #
text
my-project/
├── src/
│ ├── components/
│ │ ├── Button.js
│ │ └── Button.test.js
│ ├── setupTests.js
│ └── index.js
├── jest.config.js
└── package.json
配置文件 #
package.json #
json
{
"name": "my-project",
"version": "1.0.0",
"dependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2"
},
"devDependencies": {
"@wojtekmaj/enzyme-adapter-react-17": "^0.6.0",
"enzyme": "^3.11.0",
"jest": "^27.0.0"
},
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
}
}
jest.config.js #
javascript
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/src/setupTests.js'],
moduleNameMapper: {
'\\.(css|less|scss|sass)$': 'identity-obj-proxy',
'\\.(jpg|jpeg|png|gif|svg)$': '<rootDir>/__mocks__/fileMock.js'
},
testMatch: [
'**/__tests__/**/*.js',
'**/*.test.js'
],
collectCoverageFrom: [
'src/**/*.{js,jsx}',
'!src/index.js',
'!src/setupTests.js'
]
};
setupTests.js #
javascript
import Enzyme from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
Enzyme.configure({ adapter: new Adapter() });
// 全局配置
global.console = {
...console,
error: jest.fn(),
warn: jest.fn()
};
不同 React 版本的配置 #
React 16 配置 #
bash
npm install --save-dev enzyme enzyme-adapter-react-16
javascript
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() });
React 16.8+ 配置(支持 Hooks) #
bash
npm install --save-dev enzyme enzyme-adapter-react-16.3
javascript
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16.3';
Enzyme.configure({ adapter: new Adapter() });
React 17 配置 #
bash
npm install --save-dev enzyme @wojtekmaj/enzyme-adapter-react-17
javascript
import Enzyme from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
Enzyme.configure({ adapter: new Adapter() });
React 18 配置 #
bash
npm install --save-dev enzyme @cfaester/enzyme-adapter-react-18
javascript
import Enzyme from 'enzyme';
import Adapter from '@cfaester/enzyme-adapter-react-18';
Enzyme.configure({ adapter: new Adapter() });
Create React App 集成 #
CRA 项目配置 #
Create React App 已经内置了 Jest,只需添加 Enzyme:
bash
# 安装依赖
npm install --save-dev enzyme @wojtekmaj/enzyme-adapter-react-17
javascript
// src/setupTests.js(CRA 会自动加载此文件)
import Enzyme from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
Enzyme.configure({ adapter: new Adapter() });
TypeScript 支持 #
bash
# 安装类型定义
npm install --save-dev @types/enzyme
typescript
// src/setupTests.ts
import Enzyme from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
Enzyme.configure({ adapter: new Adapter() });
// 添加全局类型声明
declare global {
namespace NodeJS {
interface Global {
enzyme: typeof Enzyme;
}
}
}
Webpack 项目集成 #
安装依赖 #
bash
npm install --save-dev enzyme enzyme-adapter-react-16 jest babel-jest
Babel 配置 #
javascript
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', { targets: { node: 'current' } }],
'@babel/preset-react'
]
};
Jest 配置 #
javascript
// jest.config.js
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/setupTests.js'],
transform: {
'^.+\\.jsx?$': 'babel-jest'
}
};
Vite 项目集成 #
安装依赖 #
bash
npm install --save-dev enzyme @wojtekmaj/enzyme-adapter-react-17 jest jest-environment-jsdom
Jest 配置 #
javascript
// jest.config.js
module.exports = {
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['<rootDir>/setupTests.js'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1'
}
};
编写第一个测试 #
创建组件 #
javascript
// src/components/Button.js
import React from 'react';
function Button({ text, onClick }) {
return (
<button className="btn" onClick={onClick}>
{text}
</button>
);
}
export default Button;
编写测试 #
javascript
// src/components/Button.test.js
import React from 'react';
import { shallow } from 'enzyme';
import Button from './Button';
describe('Button', () => {
it('renders with correct text', () => {
const wrapper = shallow(<Button text="Click me" />);
expect(wrapper.text()).toBe('Click me');
});
it('has correct className', () => {
const wrapper = shallow(<Button text="Click" />);
expect(wrapper.hasClass('btn')).toBe(true);
});
it('handles click events', () => {
const mockClick = jest.fn();
const wrapper = shallow(<Button text="Click" onClick={mockClick} />);
wrapper.simulate('click');
expect(mockClick).toHaveBeenCalledTimes(1);
});
});
运行测试 #
bash
# 运行所有测试
npm test
# 监听模式
npm test -- --watch
# 生成覆盖率报告
npm test -- --coverage
常见问题解决 #
问题 1:适配器未配置 #
text
Error: Enzyme Internal Error: `configure` was called but `adapter` was not configured
解决方案:
javascript
// 确保 setupTests.js 被正确加载
// 检查 jest.config.js 中的 setupFilesAfterEnv 配置
问题 2:React 版本不匹配 #
text
Error: React version not supported
解决方案:
bash
# 检查 React 版本
npm list react
# 安装对应的适配器
# React 16 -> enzyme-adapter-react-16
# React 17 -> @wojtekmaj/enzyme-adapter-react-17
# React 18 -> @cfaester/enzyme-adapter-react-18
问题 3:jsdom 环境问题 #
text
Error: The element you are trying to mount is not a valid DOM element
解决方案:
javascript
// jest.config.js
module.exports = {
testEnvironment: 'jsdom'
};
问题 4:CSS 模块导入问题 #
text
SyntaxError: Unexpected token .
解决方案:
bash
npm install --save-dev identity-obj-proxy
javascript
// jest.config.js
module.exports = {
moduleNameMapper: {
'\\.module\\.css$': 'identity-obj-proxy'
}
};
问题 5:静态资源导入问题 #
text
SyntaxError: Unexpected string
解决方案:
javascript
// __mocks__/fileMock.js
module.exports = 'test-file-stub';
javascript
// jest.config.js
module.exports = {
moduleNameMapper: {
'\\.(jpg|jpeg|png|gif|svg)$': '<rootDir>/__mocks__/fileMock.js'
}
};
扩展配置 #
添加全局断言方法 #
javascript
// setupTests.js
import Enzyme from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
Enzyme.configure({ adapter: new Adapter() });
// 自定义全局方法
expect.extend({
toBeComponent(received, component) {
const pass = received.is(component);
return {
pass,
message: () => `expected ${received} ${pass ? 'not ' : ''}to be ${component}`
};
}
});
配置快照序列化 #
bash
npm install --save-dev enzyme-to-json
javascript
// setupTests.js
import { createSerializer } from 'enzyme-to-json';
expect.addSnapshotSerializer(createSerializer({ mode: 'deep' }));
javascript
// Button.test.js
import React from 'react';
import { shallow } from 'enzyme';
import Button from './Button';
it('matches snapshot', () => {
const wrapper = shallow(<Button text="Click" />);
expect(wrapper).toMatchSnapshot();
});
配置测试覆盖率 #
javascript
// jest.config.js
module.exports = {
collectCoverage: true,
coverageDirectory: 'coverage',
coverageReporters: ['text', 'lcov', 'html'],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
};
下一步 #
现在你已经完成了 Enzyme 的安装和配置,接下来学习 浅层渲染 开始编写组件测试!
最后更新:2026-03-28