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

Jest测试通过但代码未覆盖:React-Typescript中分支覆盖问题

解决React-Typescript项目Jest测试代码覆盖问题

问题背景

React-Typescript项目中,提交按钮触发的saveData函数包含try/catch/else分支:

  • try块内包含setTimeout操作,用于控制加载状态
  • 组件通过{ isLoading && <CustomSpinner isLoading={loading}/> }条件渲染加载动画
  • 当前Jest测试用例已通过,但Icov报告显示try块内的setTimeout代码、catch块及else分支未被覆盖

现有测试用例的问题

你的测试代码存在以下无效或缺失逻辑:

  • 重复调用jest.useFakeTimers(),且单独渲染CustomSpinner的代码与主组件测试无关,属于冗余操作
  • 仅覆盖了checkRollNumberExist返回false的路径,未覆盖else分支(返回true的场景)
  • 未模拟saveStudentInfo抛出错误的场景,导致catch分支完全未被触发
  • 对setTimeout的处理未正确等待内部逻辑执行,导致try块内的代码未被统计到覆盖

修复方案

需要拆分出三个独立的测试用例,分别覆盖try(含setTimeout)、else、catch分支:

1. 覆盖try分支(含setTimeout逻辑)

it("should handle successful submission with loading state", async () => {
  jest.useFakeTimers();
  jest.spyOn(mockService, "saveStudentInfo").mockResolvedValue(undefined);
  jest.spyOn(mockService, "checkRollNumberExist").mockResolvedValue(false);
  
  const { getByText, getByTestId, queryByTestId } = render(<StudentEnrollmentFrom />);
  const submitButton = getByText("Submit");

  // 点击提交,触发loading状态
  fireEvent.click(submitButton);
  expect(queryByTestId("custom-spinner")).toBeInTheDocument(); // 验证加载动画显示

  // 快进所有定时器,触发setTimeout内的逻辑
  act(() => {
    jest.runAllTimers();
  });
  await waitFor(() => {
    expect(queryByTestId("custom-spinner")).not.toBeInTheDocument(); // 验证加载动画消失
    expect(mockService.saveStudentInfo).toHaveBeenCalled(); // 验证保存接口被调用
  });

  jest.useRealTimers();
});

2. 覆盖else分支(学号已存在场景)

it("should show error when roll number exists", async () => {
  jest.spyOn(mockService, "checkRollNumberExist").mockResolvedValue(true);
  
  const { getByText, getByTestId } = render(<StudentEnrollmentFrom />);
  const submitButton = getByText("Submit");

  fireEvent.click(submitButton);
  await waitFor(() => {
    expect(getByText("Roll number already exists")).toBeInTheDocument(); // 验证错误提示
    expect(mockService.saveStudentInfo).not.toHaveBeenCalled(); // 验证保存接口未被调用
  });
});

3. 覆盖catch分支(保存接口抛出错误场景)

it("should handle submission error", async () => {
  jest.useFakeTimers();
  jest.spyOn(mockService, "saveStudentInfo").mockRejectedValue(new Error("Save failed"));
  jest.spyOn(mockService, "checkRollNumberExist").mockResolvedValue(false);
  
  const { getByText, queryByTestId } = render(<StudentEnrollmentFrom />);
  const submitButton = getByText("Submit");

  fireEvent.click(submitButton);
  expect(queryByTestId("custom-spinner")).toBeInTheDocument();

  act(() => {
    jest.runAllTimers();
  });
  await waitFor(() => {
    expect(queryByTestId("custom-spinner")).not.toBeInTheDocument();
    expect(getByText("Failed to save student info")).toBeInTheDocument(); // 验证错误提示
  });

  jest.useRealTimers();
});

关键注意事项

  • 每个测试用例结束后调用jest.useRealTimers(),避免影响其他测试
  • 使用waitFor等待异步状态更新完成,确保断言时机正确
  • 针对不同分支的核心逻辑添加断言(比如接口调用情况、状态变化、UI提示)

内容的提问来源于stack exchange,提问作者Mr. Learner

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 03:17:06