React Testing Library中Mock自定义Hook渲染组件失败求助
问题排查:React组件测试Mock自定义Hook后无法找到预期文本
问题描述
我在测试一个使用自定义Hook(useCollection)的React组件时遇到问题。该Hook从Firestore数据库获取数据并返回对象数组,组件通过array.map将数据渲染为列表。我编写测试用例Mock了该Hook,使其返回包含{id:"Belfast"}的对象数组,但测试报错TestingLibraryElementError: Unable to find an element with the text: Belfast.,无法找到预期文本。
组件代码
import React from "react"; import useCollection from "../CustomHooks/UseCollection"; export default function MyComponent() { const { collectionData } = useCollection('Location', null); return ( <div> <ul>{collectionData.map((item) => (<li key={item.id}>{item.id}</li>))}</ul> </div> ); }
原测试代码
import { render, screen, waitFor } from '@testing-library/react'; import MyComponent from './MyComponent'; import useCollection from '../CustomHooks/UseCollection'; it('render my component', () => { screen.debug() const mockUseCollection = jest.fn(() => ({ collectionData: [{id:"Belfast"},{id:"Derry"}], })); jest.mock('../CustomHooks/UseCollection', () => ({ useCollection: mockUseCollection, })); const { getByText } = render(<MyComponent />); expect(getByText("Belfast")).toBeInTheDocument(); })
问题原因
jest.mock执行时机错误:jest.mock会被自动提升到模块代码最顶部执行,在测试函数内部调用时,组件已经导入了原始的useCollectionHook,导致Mock未生效。- Mock导出方式不匹配:组件中
useCollection是默认导入,但原测试代码按命名导出的方式Mock,导致组件实际使用的还是原始Hook,而非自定义的mock函数。
解决方案
方案1:将jest.mock移到模块顶部,匹配默认导出
调整测试代码结构,确保Mock在组件导入前执行,同时正确模拟默认导出:
import { render, screen } from '@testing-library/react'; // 先执行Mock,再导入组件 jest.mock('../CustomHooks/UseCollection', () => ({ __esModule: true, // 标记为ES模块,处理默认导出逻辑 default: jest.fn(() => ({ collectionData: [{id:"Belfast"},{id:"Derry"}], })), })); import MyComponent from './MyComponent'; it('renders the component with mocked data', () => { render(<MyComponent />); expect(screen.getByText("Belfast")).toBeInTheDocument(); expect(screen.getByText("Derry")).toBeInTheDocument(); })
方案2:动态Mock(适用于多测试用例需不同返回值的场景)
如果需要在不同测试用例中返回不同的Mock数据,可使用jest.doMock并重新导入组件:
import { render, screen } from '@testing-library/react'; it('renders the component with mocked data', () => { const mockUseCollection = jest.fn(() => ({ collectionData: [{id:"Belfast"},{id:"Derry"}], })); jest.doMock('../CustomHooks/UseCollection', () => ({ __esModule: true, default: mockUseCollection, })); // 重新导入组件,确保使用Mock后的Hook const MyComponent = require('./MyComponent').default; render(<MyComponent />); expect(screen.getByText("Belfast")).toBeInTheDocument(); })
注意事项
- 确保
collectionData初始值不为空:若真实Hook在加载阶段返回空数组,Mock需保证返回有数据的数组,避免map调用报错。 - 优先使用
screen.getByText:这是Testing Library推荐的写法,无需解构,更直观。
内容的提问来源于stack exchange,提问作者gavin davis
相关产品推荐
相关产品推荐

