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

测试含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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 03:09:44