如何在Vitest中Mock React Query的useFetchQueries Hook并测试组件
在Vitest中Mock自定义Hook测试Product组件
问题背景
我写了下面这些React组件和自定义Hook代码,想在Vitest里Mock useFetchQueries返回的数据来测试Product组件,已经写了部分Mock代码,但不知道怎么正确用到测试里。
Product组件代码
export const Product = () => { const navigate = useNavigate() const { id } = useParams() const { data } = useFetchQueries() return ( <Modal> <Box> {data.data .filter((elem: TableElemType) => elem.id.toString() === id) .map((elem: TableElemType) => { return <ProductData data={elem} key={elem.id} /> })} </Box> </Modal> ) }
useFetchQueries Hook代码
export const useFetchQueries = () => { const fetcher = async () => { const res = await fetch(`https://reqres.in/api/products`) .then((res) => res.json()) .then((data) => { return data }) .catch((err) => { return err.message }) console.log(res) return res } return useQuery(['products'], async () => fetcher()) }
我写的Mock代码
vi.mock('./useFetchQueries', () => ({ useFetchQueries: vi.fn().mockReturnValue({ data: { data: ['string'] } }), })) describe('ProducData', () => { it('if renders proper data', async () => { render(renderWithRouter(<Product />, '/5')) expect(useFetchQueries).toHaveBeenCalled() }) })
解决方案
1. 修正Mock数据结构
你的useFetchQueries返回的是react-query的useQuery结果,而真实接口返回的data.data是产品对象数组,所以Mock的数据要和真实结构匹配,不能用['string'],得换成符合TableElemType的对象数组:
vi.mock('./useFetchQueries', () => ({ useFetchQueries: vi.fn().mockReturnValue({ data: { data: [ { id: 5, name: '测试产品5', /* 其他符合TableElemType的字段 */ }, { id: 6, name: '测试产品6', /* 其他字段 */ } ] } }) }))
2. 完善测试逻辑
要确保路由参数正确传递,等待组件渲染完成,还要断言内容是否正确渲染:
import { render, screen, waitFor } from '@testing-library/react' import { Product } from './Product' import { useFetchQueries } from './useFetchQueries' import renderWithRouter from './path-to-your-renderWithRouter' vi.mock('./useFetchQueries') describe('Product组件', () => { it('渲染匹配ID的产品数据', async () => { // TypeScript类型断言,避免类型报错 const mockUseFetchQueries = useFetchQueries as jest.Mock mockUseFetchQueries.mockReturnValue({ data: { data: [ { id: 5, name: '测试产品5', description: '这是测试产品' }, { id: 6, name: '测试产品6', description: '另一个测试产品' } ] } }) // 用你封装的renderWithRouter渲染组件,传递路径/5(对应id=5) render(renderWithRouter(<Product />, '/5')) // 等待异步数据渲染完成,查询匹配的产品名称 const targetProduct = await screen.findByText('测试产品5') expect(targetProduct).toBeInTheDocument() // 断言不渲染ID不匹配的产品 expect(screen.queryByText('测试产品6')).not.toBeInTheDocument() // 验证Hook确实被调用了 expect(mockUseFetchQueries).toHaveBeenCalled() }) })
3. 注意点
- 如果你的组件会处理
useQuery的加载状态(比如显示loading),可以在Mock里加上isLoading: false,避免组件一直处于加载状态 - 确保
renderWithRouter正确配置了React Router环境,比如用<MemoryRouter>包裹组件,并且能正确解析路径参数 - TypeScript项目里,需要对Mock的Hook做类型断言,避免类型不兼容的报错
内容的提问来源于stack exchange,提问作者strB
相关产品推荐
相关产品推荐

