测试含Google reCAPTCHA V3的React组件时execute断言失败
问题根因
测试失败的核心原因是你对grecaptcha.ready的mock存在逻辑缺失:jest.fn()默认生成的是无任何实现的空函数,当组件代码调用grecaptcha.ready(回调函数)时,这个mock函数只会记录自己被调用了,不会主动执行传入的回调,因此回调内部包裹的grecaptcha.execute、后续API调用逻辑永远不会触发,断言自然失败。
除此之外你的测试代码还有两个小问题会导致后续断言报错:
- 最后断言
history.goBack时写了expect(history.goBack()).toHaveBeenCalled(),括号会主动执行一次mock函数,而非断言它被业务逻辑调用,写法错误。 history.goBack被包裹在1000ms的setTimeout中,测试环境默认不会自动等待定时器执行,会导致断言超时。
修复方案
1. 补全grecaptcha.ready的mock实现
修改beforeEach中对grecaptcha的mock逻辑,让ready方法接收到回调后立即执行回调,同时启用假定时器处理延时逻辑:
beforeEach(() => { global.grecaptcha = { // 新增实现:接收到回调后立刻执行 ready: jest.fn((callback) => callback()), execute: jest.fn() }; global.grecaptcha.execute.mockResolvedValueOnce(token); process.env = { ...originalEnv, RECAPTCHA_V3_KEY: "fake-key" }; history = { goBack: jest.fn(() => "go back") }; // 启用假定时器处理setTimeout jest.useFakeTimers(); }); afterEach(() => { process.env = originalEnv; // 测试结束后恢复真定时器 jest.useRealTimers(); });
2. 调整测试用例的异步断言逻辑
点击提交按钮后,先等待grecaptcha.execute被调用,再等待API请求完成,之后快进定时器触发路由返回,最后做对应断言,修正后的测试用例如下:
test("successfully sends message", async () => { sendIt.mockResolvedValueOnce({ data: "data" }); render(<SendEmail history={history} />); const toInput = screen.getByLabelText("to"); const messageTextArea = screen.getByLabelText("message"); const sendButton = screen.getByRole("button"); user.type(toInput, "me@mail.com"); user.type(messageTextArea, "Hello there!"); user.click(sendButton); const grecaptcha = global.grecaptcha; expect(grecaptcha.ready).toHaveBeenCalled(); // 等待execute被调用 await waitFor(() => expect(grecaptcha.execute).toHaveBeenCalled()); expect(grecaptcha.execute).toHaveBeenCalledWith("fake-key", { action: "action" }); // 等待API调用完成 await waitFor(() => expect(sendIt).toHaveBeenCalled()); expect(sendIt).toHaveBeenCalledWith("me@mail.com", "Hello there!", token); // 快进1000ms触发setTimeout里的路由返回 jest.advanceTimersByTime(1000); // 修正断言写法,不要主动调用goBack expect(history.goBack).toHaveBeenCalled(); });
调整后所有逻辑都会按预期执行,测试可以正常通过。
内容的提问来源于stack exchange,提问作者Kingsley
相关产品推荐
相关产品推荐

