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

如何解决React Testing Library测试useEffect触发组件更新失败问题?

问题根因
  • 错误Mock了React.useState:你在beforeEach里把React.useState替换成了返回固定productsList和无效setter的假实现,组件内部调用setproductsList根本无法更新状态,也不会触发重渲染。
  • 错误覆盖了按钮点击事件:你自定义了全局handleSizeClick mock,还手动给XL按钮的onclick赋值为这个空mock,组件原本的handleSizeClick逻辑完全没有执行,后续的navigate、参数更新、过滤逻辑都不会触发。
  • useLocation的Mock是静态的:就算navigate调用了,useLocation返回的search值永远是空,监听queryObj.size的useEffect检测不到参数变化,不会执行过滤逻辑。
  • 断言时机错误:过滤逻辑在useEffect中异步执行,你同步断言的时候状态还没更新。
修复方案
  1. 先删除所有无效的错误Mock:移除自定义的handleSizeClick变量、移除React.useState的Mock、不要手动覆盖按钮的onclick属性。
  2. 正确处理路由Mock:推荐使用react-router-dom自带的MemoryRouter包裹测试组件,无需手动mock useLocation和useNavigate,更贴近真实运行场景。
  3. 等待异步状态更新后再断言,使用waitFor或者findBy*查询方法等待结果更新。

修改后的测试代码

import React from "react";
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
import "@testing-library/jest-dom";
import Content from "./Content";
import httpService from "../../services/httpService";
import { productsList } from "../../mockData/productsList";
import { MemoryRouter } from "react-router-dom";

const mockData = [...productsList];
jest.mock("../../services/httpService");

const mockSelector = jest.fn();
const mockDispatch = jest.fn();
jest.mock("react-redux", () => ({
  ...jest.requireActual("react-redux"),
  useSelector: () => mockSelector,
  useDispatch: () => mockDispatch
}));

describe("product should be render in Content component correctly", () => {
  beforeAll(() => {
    httpService.get.mockImplementation(() => Promise.resolve(mockData));
  });
  beforeEach(() => {
    jest.spyOn(window, "alert").mockImplementation(() => {});
  });
  afterEach(() => {
    jest.clearAllMocks();
  });
  it("should render products at the content", async () => {
    const url = "/products";
    render(
      <MemoryRouter initialEntries={[url]}>
        <Content />
      </MemoryRouter>
    );
    expect(httpService.get).toHaveBeenCalledTimes(1);
    expect(httpService.get).toHaveBeenCalledWith(url);
    expect(screen.getByText("Sort by")).toBeInTheDocument();
    const countText = await screen.findByText(/found/g);
    expect(countText.textContent).toBe("10 products found");
  });
  it("the handleSizeClick is call when XL button is clicked", async () => {
    render(
      <MemoryRouter initialEntries={["/products"]}>
        <Content />
      </MemoryRouter>
    );
    const XLButton = await screen.findByTestId("XL");
    fireEvent.click(XLButton);
    await waitFor(() => {
      expect(window.location.search).toContain("size=XL");
    })
  });
  it("the productsList is filtered when XL button is clicked", async () => {
    render(
      <MemoryRouter initialEntries={["/products"]}>
        <Content />
      </MemoryRouter>
    );
    const XLButton = await screen.findByTestId("XL");
    fireEvent.click(XLButton);
    await waitFor(() => {
      const countText = screen.getByText(/found/g);
      expect(countText.textContent).toBe("5 products found");
    })
  });
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 03:15:05