如何使用Jest测试含嵌套组件与异步调用的React组件?
测试React天气仪表盘组件的Jest方案
针对你的天气仪表盘组件,完全可以用Jest结合React Testing Library完成测试,包括模拟数据请求、验证状态更新和组件渲染逻辑。下面分两种常见方案详细说明:
一、模拟全局fetch请求(直接适配组件现有代码)
你的组件直接使用了原生fetch,可以在测试文件中全局模拟这个API,无需修改原组件代码。
1. 准备测试依赖
确保已安装@testing-library/react和@testing-library/jest-dom(通常创建React项目时已自带)。
2. 编写测试用例(Dashboard.test.js)
import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import Dashboard from './Dashboard'; // 模拟测试用的天气数据 const mockWeatherData = [ { timestamp: '2024-05-01 10:00', temperature: 25, dewPoint: 18, visibility: 10 }, { timestamp: '2024-05-01 11:00', temperature: 27, dewPoint: 19, visibility: 9 }, { timestamp: '2024-05-01 12:00', temperature: 29, dewPoint: 20, visibility: 8 }, ]; // 全局模拟fetch beforeEach(() => { global.fetch = jest.fn(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockWeatherData), }) ); }); // 重置mock afterEach(() => { jest.clearAllMocks(); }); test('初始渲染显示无数据提示,不渲染图表', () => { render(<Dashboard />); // 验证无数据提示存在 expect(screen.getByTestId('no-charts')).toBeInTheDocument(); // 验证图表容器不存在 expect(screen.queryByTestId('charts')).not.toBeInTheDocument(); }); test('选择下拉选项后,触发请求、更新状态并渲染图表', async () => { render(<Dashboard />); // 找到下拉框容器并触发选择操作 const selectContainer = screen.getByTestId('Select'); fireEvent.change(selectContainer, { target: { value: 'temperature' } }); // 验证fetch被正确调用 expect(fetch).toHaveBeenCalledTimes(1); expect(fetch).toHaveBeenCalledWith('http://localhost:3000/getData'); // 等待异步操作完成和组件重新渲染 await waitFor(() => { // 验证无数据提示消失 expect(screen.queryByTestId('no-charts')).not.toBeInTheDocument(); // 验证图表容器存在 expect(screen.getByTestId('charts')).toBeInTheDocument(); // 验证BarChart接收了正确的props const barChart = screen.getByTestId('BarChart'); expect(barChart).toHaveAttribute('label', 'temperature'); expect(barChart).toHaveAttribute('labels', JSON.stringify(mockWeatherData.map(item => item.timestamp))); expect(barChart).toHaveAttribute('selectedData', JSON.stringify(mockWeatherData.map(item => item.temperature))); }); }); test('请求失败时抛出对应错误', async () => { // 重写fetch模拟,返回失败响应 global.fetch = jest.fn(() => Promise.resolve({ ok: false, status: 500, }) ); render(<Dashboard />); const selectContainer = screen.getByTestId('Select'); // 触发选择并捕获错误 await expect(fireEvent.change(selectContainer, { target: { value: 'temperature' } })).rejects.toThrow('An error has occured: 500'); });
二、使用jest.mock()模拟独立数据请求模块(推荐架构优化)
如果把数据请求逻辑抽离到单独的模块,用jest.mock()可以更灵活地模拟该模块,便于后续测试维护。
1. 重构原组件的请求逻辑
创建api.js:
export const fetchWeatherData = async () => { const response = await fetch('http://localhost:3000/getData'); if (!response.ok) { const message = `An error has occured: ${response.status}`; throw new Error(message); } return response.json(); };
修改Dashboard.js中的handleChange:
import { fetchWeatherData } from './api'; // ... const handleChange = async (selectedOption: any) => { const jsonData = await fetchWeatherData(); setLabel(selectedOption.value); setLabels(jsonData.map((item: { timestamp: any; }) => item.timestamp)); // 其余状态更新逻辑保持不变 };
2. 编写测试用例
import { render, screen, fireEvent, waitFor } from '@testing-library/react'; import Dashboard from './Dashboard'; import { fetchWeatherData } from './api'; // 模拟api模块 jest.mock('./api'); const mockWeatherData = [ { timestamp: '2024-05-01 10:00', temperature: 25, dewPoint: 18, visibility: 10 }, ]; test('选择选项后调用模拟API并渲染对应图表', async () => { // 设置mock返回值 (fetchWeatherData as jest.Mock).mockResolvedValue(mockWeatherData); render(<Dashboard />); fireEvent.change(screen.getByTestId('Select'), { target: { value: 'dewPoint' } }); await waitFor(() => { expect(fetchWeatherData).toHaveBeenCalled(); const radarChart = screen.getByTestId('RadarChart'); expect(radarChart).toHaveAttribute('selectedData', JSON.stringify(mockWeatherData.map(item => item.dewPoint))); }); });
关键注意事项
- 使用
waitFor等待异步操作完成(状态更新、组件重新渲染),避免测试因时序问题失败。 - 利用组件中的
data-testid快速定位元素,这是测试React组件的最佳实践之一。 - 每次测试后用
jest.clearAllMocks()重置mock,避免测试用例之间的干扰。
内容的提问来源于stack exchange,提问作者Roh
相关产品推荐
相关产品推荐

