React Testing Library 中如何正确模拟异步数据加载?

A. 婷婷 阅读 23

我在用 React Testing Library 测试一个组件,它会在 useEffect 里调用 API 获取数据。我用 jest.mock 模拟了请求,但测试总是报错说找不到加载后的元素,是不是没等异步完成?

我试过用 await waitFor(() => expect(...).toBeInTheDocument()),但有时候还是失败,控制台还提示 “act” 相关的警告。下面是我的测试代码:

test('显示用户信息', async () => {
  render(<UserProfile userId="123" />);
  await waitFor(() => {
    expect(screen.getByText('John Doe')).toBeInTheDocument();
  });
});

API mock 是这样写的:

jest.mock('../api', () => ({
  fetchUser: () => Promise.resolve({ name: 'John Doe' })
}));

到底该怎么写才能稳定通过测试啊?

我来解答 赞 3 收藏
二维码
手机扫码查看
2 条解答
风珍的笔记
你的问题在于 mock 的 Promise 解决得太快了,React 还没来得及渲染就结束了,导致测试找不到元素。

两个关键改动:

第一,把 getByText 改成 findByText,它本身就是异步的,会自动等待元素出现:

test('显示用户信息', async () => {
render();
const userName = await screen.findByText('John Doe');
expect(userName).toBeInTheDocument();
});


第二,让你的 mock 带一点延迟,模拟真实 API 的行为:

jest.mock('../api', () => ({
fetchUser: () => new Promise(resolve => {
setTimeout(() => {
resolve({ name: 'John Doe' });
}, 100);
})
}));


这样写的好处是给 React 足够的时间处理状态更新,act 警告自然就没了。

如果你的 API 在组件里是这样调用的:

useEffect(() => {
fetchUser(userId).then(data => setUser(data));
}, [userId]);


那上面两种改法足够让测试稳定通过。findByText 默认会等 1000ms,足够cover大多数场景。
点赞
2026-03-19 15:12
シ顺红
シ顺红 Lv1
你的问题核心在于 mock 的写法,Promise.resolve() 没问题,但 React Testing Library 在等待异步状态更新时需要更稳妥的方式。

问题出在两个地方:

第一个,mock 需要返回真正的 Promise

jest.mock('../api', () => ({
fetchUser: jest.fn(() => Promise.resolve({ name: 'John Doe' }))
}));


用 jest.fn() 包装一下,这样能确保 mock 被调用时返回的是真正的 Promise。

第二个,推荐用 findBy 替代 waitFor + getBy

findBy 本身就是异步的,会自动等待,比你手动写 waitFor 稳定:

test('显示用户信息', async () => {
render(<UserProfile userId="123" />);
const userName = await screen.findByText('John Doe');
expect(userName).toBeInTheDocument();
});


findBy 内部已经帮你做了 waitFor 的事情,而且默认超时时间是 1000ms,足够大多数场景用。

如果还是有问题,加个 assert 进去看看到底返回了什么

test('显示用户信息', async () => {
const { fetchUser } = require('../api');

render(<UserProfile userId="123" />);

// 先确认 mock 被调用了
expect(fetchUser).toHaveBeenCalledWith('123');

const userName = await screen.findByText('John Doe');
expect(userName).toBeInTheDocument();
});


act 警告通常是因为组件渲染时触发的状态更新没有被正确等待,findBy 能解决大部分这种情况。如果你的 UserProfile 组件在加载状态会先显示 loading 文字,可以先 assert loading 消失,再 assert 数据出现,这样更符合实际用户场景。
点赞
2026-03-19 09:22