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

前端测试问题:点击后禁用的按钮Jest断言失败排查

问题:React倒计时按钮Jest断言禁用状态失败

问题背景

我开发了一个点击后禁用并启动60秒倒计时的按钮,React业务代码运行正常,但编写Jest测试用例时,断言按钮点击后处于禁用状态却失败,报错显示元素未禁用。

业务代码

组件JSX部分

<GeneralButton variant="contained" disabled={state} onClick={onBtnClick}>
 Resend verification email
 <br></br>
 {state ? seconds : ""}
</GeneralButton>

状态与逻辑部分

const [state, setState] = useState(false);
const [seconds, setSeconds] = useState(60);
useEffect(() => {
  let secondsInterval: string | number | NodeJS.Timeout | undefined;
  if (state === true) {
    secondsInterval = setInterval(() => {
      setSeconds((prev) => prev - 1);
    }, 1000);
  }
  return () => clearInterval(secondsInterval);
}, [state]);
const onBtnClick = () => {
  setSeconds(60);
  setState(true);
  setTimeout(() => {
    setState(false);
  }, 60000);
};

测试代码

describe("<EmailVerificationPage />", () => {
  it("button should be disabled when clicked", () => {
    render(<EmailVerificationPage />);
    userEvent.click(screen.getByRole("button", { name: "Resend verification email" }));
    // eslint-disable-next-line jest/valid-expect
    expect(screen.getByText("Resend verification email").closest("button")).toBeDisabled();
  });
});

报错信息

Received element is not disabled:
      <button class="MuiButtonBase-root MuiButton-root MuiButton-contained MuiButton-containedPrimary MuiButton-sizeMedium MuiButton-containedSizeMedium css-1zc34d-MuiButtonBase-root-MuiButton-root" tabindex="0" type="button" />

       8 |     userEvent.click(screen.getByRole("button", { name: "Resend verification email" }));
       9 |     // eslint-disable-next-line jest/valid-expect
    > 10 |     expect(screen.getByText("Resend verification email").closest("button")).toBeDisabled();

排查与解决方法

核心原因

  • userEvent.click是异步操作,React状态更新同样是异步的,点击后立即执行断言时,组件还未完成状态更新,按钮的disabled属性尚未生效。
  • 通过getByText查找文本再用closest获取按钮的方式不够可靠,存在匹配误差的可能。

修复后的测试代码

describe("<EmailVerificationPage />", () => {
  it("button should be disabled when clicked", async () => {
    render(<EmailVerificationPage />);
    const resendButton = screen.getByRole("button", { name: "Resend verification email" });
    
    // 等待点击的异步操作完成
    await userEvent.click(resendButton);
    
    // 直接使用预获取的按钮元素断言,避免二次查找的不确定性
    expect(resendButton).toBeDisabled();
  });
});

额外优化(可选)

如果遇到状态更新延迟的情况,可以配合waitFor确保断言条件满足:

import { waitFor } from '@testing-library/react';

// ...

it("button should be disabled when clicked", async () => {
  render(<EmailVerificationPage />);
  const resendButton = screen.getByRole("button", { name: "Resend verification email" });
  
  await userEvent.click(resendButton);
  
  await waitFor(() => {
    expect(resendButton).toBeDisabled();
  });
});

内容的提问来源于stack exchange,提问作者YI LU

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 08:20:53