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秒后触发逻辑执行的结果。
修复方案
按照以下步骤调整测试代码即可:
- 启用fake定时器前,先对
setTimeout创建Jest spy,或者直接使用Jest内置的fake定时器断言方法 - 确认弹窗出现后,手动调用
jest.advanceTimersByTime(3000)快进3秒,触发定时器回调执行 - 调整断言顺序,先验证定时器调用情况,再验证弹窗是否消失
- 测试结束后清理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
相关产品推荐
相关产品推荐

