匹配器错误:接收到的值必须是模拟或间谍函数
Posted
技术标签:
【中文标题】匹配器错误:接收到的值必须是模拟或间谍函数【英文标题】:Matcher error: received value must be a mock or spy function 【发布时间】:2021-09-02 02:12:32 【问题描述】:我正在为表单 React 组件编写测试(使用 Jest 和 React 测试库)。我有一个在表单提交上运行的方法:
const onSubmit = (data) =>
// ...
setIsPopupActive(true);
// ...
;
和useEffect
在isPopupActive
更改之后运行,所以也在提交时:
useEffect(() =>
if (isPopupActive)
setTimeout(() =>
setIsPopupActive(false);
, 3000);
, [isPopupActive]);
在测试中,我想检查一下,弹出窗口是否在 3 秒后消失。所以这是我的测试:
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');
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/)
);
await waitFor(() =>
expect(popup).not.toBeInTheDocument(); // this passes
expect(setTimeout).toHaveBeenCalledTimes(1);
expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 3000);
);
);
但是,我收到了错误:
expect(received).toHaveBeenCalledTimes(expected)
Matcher error: received value must be a mock or spy function
Received has type: function
Received has value: [Function setTimeout]
我做错了什么?
【问题讨论】:
【参考方案1】:以下方法有效
beforeEach(() =>
jest.spyOn(global, 'setTimeout');
);
afterEach(() =>
global.setTimeout.mockRestore();
);
it('Test if SetTimeout is been called',
global.setTimeout.mockImplementation((callback) => callback());
expect(global.setTimeout).toBeCalledWith(expect.any(Function), 7500);
)
【讨论】:
【参考方案2】:Jest 27 对 fakeTimers 进行了重大更改。似乎 Jest 贡献者没有按时更新文档。 This 对 Github 问题的评论证实了这一点。另外,here相关公关。
嗯,你可以通过两种方式解决你的问题。
-
将 Jest 配置为使用旧版假计时器。在 jest.config.js 中,您可以添加一行(但它不适用于我):
module.exports =
// many of lines omited
timers: 'legacy'
;
-
为单独的测试套件甚至测试配置传统的假计时器:
jest.useFakeTimers('legacy');
describe('My awesome logic', () =>
// blah blah blah
);
最好使用基于@sinonjs/fake-timers 的新语法。但是我找不到 Jest 的工作示例,所以我会尽快更新这个答案。
【讨论】:
此处列出的选项 2 对我有用。我希望开发人员不要在没有相应文档的情况下发布新版本。如果文档还没有准备好,有什么急事??【参考方案3】:在您的情况下,setTimeout
不是模拟或间谍,而是一个真正的功能。要使其成为间谍,请使用const timeoutSpy = jest.spyOn(window, 'setTimeout')
。并在断言中使用timeoutSpy
。
您也可以不测试调用setTimeout
函数的事实,而是断言setIsPopupActive
被调用了一次,并且使用false
。为此,您可能需要执行 jest.runOnlyPendingTimers()
或 jest.runAllTimers()
【讨论】:
不幸的是,当我用timeoutSpy
替换setTimeout
时,它说接收到的函数根本没有被调用。 jest.runOnlyPendingTimers()
和 jest.runAllTimers
也没有帮助。还有其他选择吗?
您是否尝试将“现代”参数作为字符串传递给jest.useFakeTimers()
?
我也有同样的问题,expect(setTimeout).toHaveBeenCalledTimes(1);在jestjs.io/fr/docs/timer-mocks 中明确提到...以上是关于匹配器错误:接收到的值必须是模拟或间谍函数的主要内容,如果未能解决你的问题,请参考以下文章
C POSIX子级根据文件内容发送信号,父级将接收到的信号与文件匹配