如何为含useEffect、axios、useParams的React组件编写单元测试?
MovieDetails组件单元测试实现方案
1. 正确传递路由参数给useParams
测试时useParams拿不到movieId的核心原因是MemoryRouter未配置正确的初始路由条目。你需要在渲染组件时,通过initialEntries传入包含目标movieId的路由路径,确保useParams能正确解析参数:
import { MemoryRouter } from 'react-router-dom'; import { render, screen } from '@testing-library/react'; import MovieDetails from './MovieDetails'; test('组件挂载时能获取到正确的movieId', () => { render( <MemoryRouter initialEntries={['/movies/123']}> <MovieDetails /> </MemoryRouter> ); // 后续可结合请求mock验证逻辑是否使用了正确的movieId });
注意:initialEntries里的路径必须和组件实际绑定的路由规则一致(比如组件路由是/movies/:movieId,就必须传/movies/xxx格式的路径)。
2. Mock Axios 避免真实接口请求
直接调用真实接口会导致测试不稳定,用Jest mock掉axios,同时模拟请求的成功、失败、挂起等场景:
模拟请求成功场景
import axios from 'axios'; // 全局mock axios jest.mock('axios'); test('成功获取电影详情并渲染', async () => { // 模拟接口返回数据 const mockMovie = { id: 123, title: '星际穿越', overview: '未来地球资源枯竭...' }; axios.get.mockResolvedValue({ data: mockMovie }); render( <MemoryRouter initialEntries={['/movies/123']}> <MovieDetails /> </MemoryRouter> ); // 断言加载状态显示 expect(screen.getByText('加载中...')).toBeInTheDocument(); // 等待请求完成,断言电影标题渲染 const title = await screen.findByText('星际穿越'); expect(title).toBeInTheDocument(); // 验证axios请求的URL和参数正确 expect(axios.get).toHaveBeenCalledWith('/api/movies/123', expect.any(Object)); });
3. 测试请求取消与卸载清理逻辑
组件里用了axios的CancelToken处理卸载时的请求取消,测试时需要mockCancelToken.source来捕获取消行为:
test('组件卸载时取消未完成的请求', async () => { // 模拟CancelToken.source返回的对象,捕获cancel方法调用 const mockCancelFn = jest.fn(); axios.CancelToken.source.mockReturnValue({ token: 'mock-token', cancel: mockCancelFn }); // 让请求处于挂起状态,避免自动完成 axios.get.mockImplementation(() => new Promise(() => {})); // 渲染组件并获取卸载方法 const { unmount } = render( <MemoryRouter initialEntries={['/movies/123']}> <MovieDetails /> </MemoryRouter> ); // 触发组件卸载 unmount(); // 断言cancel方法被调用,且传入了正确的取消信息 expect(mockCancelFn).toHaveBeenCalledWith('Component unmounted'); });
4. 测试错误处理场景
模拟请求失败(非取消错误)
test('请求失败时显示错误提示', async () => { axios.get.mockRejectedValue(new Error('接口请求失败')); render( <MemoryRouter initialEntries={['/movies/123']}> <MovieDetails /> </MemoryRouter> ); // 断言错误提示渲染 const errorText = await screen.findByText('加载电影详情失败,请稍后重试'); expect(errorText).toBeInTheDocument(); });
模拟请求被取消的场景
确保组件不会把取消请求的错误当成普通错误展示:
test('请求被取消时不显示错误提示', async () => { // 创建axios取消错误实例 const cancelError = axios.Cancel('组件已卸载'); axios.get.mockRejectedValue(cancelError); render( <MemoryRouter initialEntries={['/movies/123']}> <MovieDetails /> </MemoryRouter> ); // 断言错误提示不会出现 await waitFor(() => { expect(screen.queryByText('加载电影详情失败,请稍后重试')).not.toBeInTheDocument(); }); });
内容的提问来源于stack exchange,提问作者burak mert intepe
相关产品推荐
相关产品推荐

