React组件测试时为什么mock的API没有被调用?
我在用Jest+React Testing Library测试一个组件时遇到了问题。组件里用useEffect调用了外部API,我按教程写了mock但测试总是失败:
// userApi.js
export const fetchUser = jest.fn(() => Promise.resolve({ name: 'mock' }));
// UserComponent.test.js
import { render, screen } from '@testing-library/react';
import { fetchUser } from './userApi';
import UserComponent from './UserComponent';
test('should call API on mount', async () => {
fetchUser.mockClear();
render();
expect(fetchUser).toHaveBeenCalledTimes(1); // 这里始终显示0
});
我已经确认组件确实在useEffect里调用了fetchUser,但测试时mock的函数计数一直是0。试过在测试前加jest.mock(‘./userApi’)也不行,控制台没有报错但就是没触发。是不是mock的写法有问题?
先说mock的问题,你需要在测试文件顶部加
jest.mock('./userApi'),这个mock必须在import真实组件之前执行。否则React组件会用真实的API而不是你的mock。然后因为fetchUser是异步的,你需要用
await等它执行完再断言。React Testing Library的render返回一个对象,里面有waitFor可以用。改后的测试代码应该是这样的:
几个关键点:
1.
jest.mock要放最前面,比import还靠前2. 用
waitFor包裹断言,因为useEffect里的调用是异步的3. 确保你的组件里确实在useEffect调用了fetchUser
有时候这种问题真的让人头秃,特别是当测试突然不工作但代码明明没问题的时候。下次遇到类似情况可以先用console.log看看mock函数到底有没有被调用。
userApi进行 mock。即使你写了jest.fn(),但如果模块加载时没有被正确替换掉,组件还是会调用原始的实现。我之前也碰到过这个问题。解决方法很简单:你需要在测试文件中明确地 mock 整个
userApi.js模块。像这样:重点是:
1.
jest.mock('./userApi')必须加在测试文件顶部,确保模块被正确替换。2. 重新导入
fetchUser,保证你操作的是 mock 后的对象。3. 在
render()时别忘了传入你的组件实例。试试这个修改,应该就能正常工作了!如果还是有问题,可能是其他地方有干扰,比如组件内部的逻辑。