单元测试方案完全指南
📋 概述
单元测试是保证ECharts集成代码质量的关键。通过测试图表的初始化、配置更新、事件触发、资源清理等场景,可以确保组件在各种情况下都能正常工作。
核心价值
- 质量保证:捕获回归bug
- 重构信心:修改代码后快速验证
- 文档作用:测试用例即使用示例
- 持续集成:自动化测试流程
- 团队协作:明确的预期行为
🎯 测试框架选择
Jest + Testing Library(推荐)
bash
npm install --save-dev jest @testing-library/react @testing-library/jest-dom1
🔧 React组件测试
1. 基础测试
typescript
// __tests__/ECharts.test.tsx
import { render, screen } from '@testing-library/react';
import ECharts from '../ECharts';
describe('ECharts Component', () => {
test('应该渲染图表容器', () => {
const option = {
xAxis: { type: 'category', data: ['A'] },
yAxis: { type: 'value' },
series: [{ type: 'bar', data: [10] }]
};
render(<ECharts option={option} />);
const chartContainer = document.querySelector('.echarts-container');
expect(chartContainer).toBeInTheDocument();
});
test('应该应用自定义样式', () => {
const option = { /* ... */ };
const style = { width: '800px', height: '600px' };
render(<ECharts option={option} style={style} />);
const container = document.querySelector('.echarts-container');
expect(container).toHaveStyle({ width: '800px', height: '600px' });
});
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2. Hook测试
typescript
// __tests__/useECharts.test.ts
import { renderHook, act } from '@testing-library/react';
import { useECharts } from '../useECharts';
describe('useECharts Hook', () => {
test('应该初始化图表实例', () => {
const option = {
xAxis: { type: 'category', data: ['A'] },
yAxis: { type: 'value' },
series: [{ type: 'bar', data: [10] }]
};
const { result } = renderHook(() => useECharts(option));
expect(result.current.chartRef.current).toBeDefined();
});
test('应该在配置更新时重新渲染', () => {
const initialOption = {
series: [{ type: 'bar', data: [10] }]
};
const { result, rerender } = renderHook(
({ option }) => useECharts(option),
{ initialProps: { option: initialOption } }
);
const updatedOption = {
series: [{ type: 'bar', data: [20] }]
};
act(() => {
rerender({ option: updatedOption });
});
// 验证图表已更新
expect(result.current.chartInstance).toBeDefined();
});
test('应该在卸载时销毁图表', () => {
const option = { /* ... */ };
const { unmount } = renderHook(() => useECharts(option));
unmount();
// 验证实例已被清理
// 需要通过mock验证
});
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
3. Mock ECharts
typescript
// __mocks__/echarts.ts
const mockChartInstance = {
setOption: jest.fn(),
resize: jest.fn(),
dispose: jest.fn(),
clear: jest.fn(),
showLoading: jest.fn(),
hideLoading: jest.fn(),
on: jest.fn(),
off: jest.fn(),
dispatchAction: jest.fn()
};
const echarts = {
init: jest.fn(() => mockChartInstance),
registerTheme: jest.fn(),
registerMap: jest.fn()
};
export default echarts;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
💡 Vue组件测试
typescript
// __tests__/ChartComponent.spec.ts
import { mount } from '@vue/test-utils';
import ChartComponent from '../ChartComponent.vue';
describe('ChartComponent', () => {
test('应该渲染图表', () => {
const wrapper = mount(ChartComponent, {
props: {
option: {
xAxis: { type: 'category', data: ['A'] },
yAxis: { type: 'value' },
series: [{ type: 'bar', data: [10] }]
}
}
});
expect(wrapper.find('.chart-container').exists()).toBe(true);
});
test('应该在props变化时更新', async () => {
const wrapper = mount(ChartComponent, {
props: {
option: {
series: [{ type: 'bar', data: [10] }]
}
}
});
await wrapper.setProps({
option: {
series: [{ type: 'bar', data: [20] }]
}
});
// 验证更新
expect(wrapper.vm.chartInstance).toBeDefined();
});
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
🎯 最佳实践
1. 测试覆盖率目标
json
{
"jest": {
"coverageThreshold": {
"global": {
"branches": 80,
"functions": 80,
"lines": 80,
"statements": 80
}
}
}
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
2. 快照测试
typescript
test('应该匹配快照', () => {
const option = { /* ... */ };
const { container } = render(<ECharts option={option} />);
expect(container).toMatchSnapshot();
});1
2
3
4
5
6
2
3
4
5
6
3. 异步测试
typescript
test('应该处理异步数据加载', async () => {
const fetchData = jest.fn(() => Promise.resolve([10, 20, 30]));
const { result } = renderHook(() => {
const [data, setData] = useState([]);
useEffect(() => {
fetchData().then(setData);
}, []);
return useECharts({
series: [{ type: 'bar', data }]
});
});
await waitFor(() => {
expect(result.current.chartInstance).toBeDefined();
});
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
📊 性能指标
| 测试类型 | 执行时间 | 说明 |
|---|---|---|
| 单元测试 | <100ms | 单个测试 |
| 集成测试 | 200-500ms | 组件交互 |
| 快照测试 | 50-100ms | DOM比较 |
🔗 相关链接
💎 总结
单元测试核心价值:
- ✅ 保证代码质量
- ✅ 重构信心
- ✅ 文档作用
- ✅ CI/CD集成
关键原则:
- 测试行为而非实现:关注做什么,不是怎么做
- Mock外部依赖:隔离测试单元
- 保持测试简洁:易于理解和维护
- 高覆盖率:至少80%
掌握单元测试,让代码更可靠!✅
