You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jest测试React组件报Matcher error: received value must be a mock错误如何解决?

问题原因
  • 你调用jest.useFakeTimers()之后,没有对全局的setTimeout创建监听spy,直接对原生setTimeout调用toHaveBeenCalledTimes这类mock专属断言方法,就会抛出「received value must be a mock or spy function」的错误。
  • 另外你当前的测试逻辑存在隐患:使用fake定时器后没有主动快进时间,弹窗消失的断言通过属于偶发的异常情况,并不是按你预期的3秒后触发逻辑执行的结果。
修复方案

按照以下步骤调整测试代码即可:

  1. 启用fake定时器前,先对setTimeout创建Jest spy,或者直接使用Jest内置的fake定时器断言方法
  2. 确认弹窗出现后,手动调用jest.advanceTimersByTime(3000)快进3秒,触发定时器回调执行
  3. 调整断言顺序,先验证定时器调用情况,再验证弹窗是否消失
  4. 测试结束后清理fake定时器,避免影响其他用例

修复后的完整代码如下:

it('Closes popup after 3 seconds', async () => {
  const nameInput = screen.getByPlaceholderText('Imię');
  const emailInput = screen.getByPlaceholderText('Email');
  const messageInput = screen.getByPlaceholderText('Wiadomość');
  const submitButton = screen.getByText('Wyślij');

  // 给setTimeout创建spy
  const setTimeoutSpy = jest.spyOn(global, 'setTimeout');
  jest.useFakeTimers();

  fireEvent.change(nameInput, { target: { value: 'Test name' } });
  fireEvent.change(emailInput, { target: { value: 'test@test.com' } });
  fireEvent.change(messageInput, { target: { value: 'Test message' } });
  fireEvent.click(submitButton);

  // 校验弹窗正常弹出
  const popup = await waitFor(() =>
    screen.getByText(/Wiadomość została wysłana/)
  );
  expect(popup).toBeInTheDocument();

  // 快进3秒触发定时器回调
  jest.advanceTimersByTime(3000);

  // 校验定时器调用符合预期
  expect(setTimeoutSpy).toHaveBeenCalledTimes(1);
  expect(setTimeoutSpy).toHaveBeenLastCalledWith(expect.any(Function), 3000);

  // 校验弹窗已消失
  expect(popup).not.toBeInTheDocument();

  // 清理spy和定时器,避免影响其他用例
  setTimeoutSpy.mockRestore();
  jest.useRealTimers();
});

内容的提问来源于stack exchange,提问作者Velrin Black

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.16 07:15:03