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

React Redux购物车组件Jest测试中action无法触发问题求助

React购物车清空功能单元测试失败问题排查

问题背景

我们的React购物车组件在UI上点击「清空购物车」按钮可正常清空购物车并显示提示,但使用Jest/React Testing Library编写的单元测试中,点击按钮后clearCart reducer未触发,导致测试断言失败。


相关代码

购物车组件代码

import { useDispatch, useSelector } from "react-redux";
import ItemList from "./ItemList";
import { clearCart } from "../slice/cartSlice";

const Cart = () => {
  const dispatch = useDispatch();
  const cartItems = useSelector((store) => store.cart.items);

  const handleClearCart = () => {
    console.log("Here in the handle clear cart onClick event handler");
    console.log("clearCart function is ", clearCart);
    dispatch(clearCart());
  };

  return (
    <div>
      <h1>Cart</h1>
      {cartItems.length != 0 && (
        <button onClick={handleClearCart}>
          Clear Cart
        </button>
      )}
      {cartItems.length ? (
        <div>
          <ItemList items={cartItems} />
        </div>
      ) : (
        <h3> Your cart is empty !</h3>
      )}
    </div>
  );
};

export default Cart;

Cart Slice代码

import { createSlice, current } from "@reduxjs/toolkit";

const cartSlice = createSlice({
  name: "cart",
  initialState: { items: [] },
  reducers: {
    addItem: (state, action) => {
      state.items.push(action.payload);
    },
    removeItem: (state, action) => {
      const filteredList = state.items.filter(
        (item, index) => index != action.payload
      );
      return {
        ...state,
        items: filteredList,
      };
    },
    clearCart: (state) => {
      console.log("Here in the clear cart reducer");
      state.items.length = 0;
    },
  },
});

export const { addItem, removeItem, clearCart } = cartSlice.actions;
export default cartSlice.reducer;

单元测试代码

import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import "@testing-library/jest-dom";
import { Provider } from "react-redux";
import configureMockStore from "redux-mock-store";
import mockStoreInfo from "../__mocks__/storeMock";
import Cart from "../src/components/Cart";

test("should clear cart when click on clear cart in cart component", async () => {
  render(
    <Provider store={storeMock}>
      <Cart />
    </Provider>
  );
  const clearCartButton = screen.getByRole("button", { name: "Clear Cart" });
  expect(clearCartButton).toBeInTheDocument();
  console.log("clearCart button is", clearCartButton);
  fireEvent.click(clearCartButton);
  expect(await (screen.getByText(" Your cart is empty !"))).toBeInTheDocument();
});

问题现象

  • 测试中Clear Cart按钮可被正确识别,handleClearCart函数也能执行
  • cartSlice中clearCart reducer的日志未输出,说明reducer未被调用
  • 测试失败时DOM结构显示购物车商品未清空:
<body>
  <div>
    <div class="cart-container" data-testid="cart">
      <h1 class="cart-heading">Cart</h1>
      <button class="clear-cart" data-testid="clear-cart">Clear Cart</button>
      <div class="cart-items" data-testid="cart-items-list">
        <div>
          <div class="menu-item">
            <div class="sc-guDLey byrpfT">
              <div class="menu-header">
                <span> Al Fungi Gourmet-Pizza </span>
                <span> - ₹ 549 </span>
              </div>
              <p class="accordion-body-description">
                Gourmet mushroom pizza on a cheesy Mozzarella base with
                jalapenos & cherry tomatoes.
              </p>
            </div>
            <div class="sc-beySPh fSyZNZ">
              <img
                class="accordion-image"
                src="https://res.cloudinary.com/swiggy/image/upload/fl_lossy,f_auto,q_auto,w_508,h_320,c_fill/4ffebb537f043e94e09e928f48e640e9"
              />
              <button class="accordion-button-delete">Remove -</button>
            </div>
          </div>
          <div class="menu-item">
            <div class="sc-guDLey byrpfT">
              <div class="menu-header">
                <span> Butterscotch Mousse Cake </span>
                <span> - ₹ 103.81 </span>
              </div>
              <p class="accordion-body-description">
                Sweet temptation! Butterscotch flavored mousse
              </p>
            </div>
            <div class="sc-beySPh fSyZNZ">
              <img
                class="accordion-image"
                src="https://res.cloudinary.com/swiggy/image/upload/fl_lossy,f_auto,q_auto,w_508,h_320,c_fill/e91002ccf7239fffeceaab4956a15a3a"
              />
              <button class="accordion-button-delete">Remove -</button>
            </div>
          </div>
          <div class="menu-item">
            <div class="sc-guDLey byrpfT">
              <div class="menu-header">
                <span> Choco Lava Cake </span>
                <span> - ₹ 109 </span>
              </div>
              <p class="accordion-body-description">
                Chocolate lovers delight! Indulgent, gooey molten lava inside
                chocolate cake
              </p>
            </div>
            <div class="sc-beySPh fSyZNZ">
              <img
                class="accordion-image"
                src="https://res.cloudinary.com/swiggy/image/upload/fl_lossy,f_auto,q_auto,w_508,h_320,c_fill/517791a68253a9a5084664b470f6e115"
              />
              <button class="accordion-button-delete">Remove -</button>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</body>

问题原因及解决方案

核心原因

redux-mock-store创建的模拟商店仅记录action派发,不会实际执行reducer逻辑更新状态。因此点击按钮后,action虽被派发,但store状态未改变,组件不会重新渲染,导致断言失败。

解决方案1:使用真实Redux Store测试

直接用Redux Toolkit的configureStore创建包含真实reducer的测试store,确保状态能正确更新:

import { fireEvent, render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import { Provider } from "react-redux";
import { configureStore } from "@reduxjs/toolkit";
import cartReducer from "../src/slice/cartSlice";
import Cart from "../src/components/Cart";

test("should clear cart when click on clear cart in cart component", () => {
  // 创建带初始商品的真实测试store
  const store = configureStore({
    reducer: {
      cart: cartReducer,
    },
    preloadedState: {
      cart: {
        items: [
          { name: "Al Fungi Gourmet-Pizza", price: 549 },
          { name: "Butterscotch Mousse Cake", price: 103.81 },
          { name: "Choco Lava Cake", price: 109 },
        ],
      },
    },
  });

  render(
    <Provider store={store}>
      <Cart />
    </Provider>
  );

  const clearCartButton = screen.getByRole("button", { name: "Clear Cart" });
  expect(clearCartButton).toBeInTheDocument();

  fireEvent.click(clearCartButton);

  // Redux状态更新同步,无需await
  expect(screen.getByText(" Your cart is empty !")).toBeInTheDocument();
});

解决方案2:手动更新Mock Store状态(不推荐)

若坚持使用redux-mock-store,需手动更新store状态并重新渲染组件:

import { fireEvent, render, screen } from "@testing-library/react";
import "@testing-library/jest-dom";
import { Provider } from "react-redux";
import configureMockStore from "redux-mock-store";
import mockStoreInfo from "../__mocks__/storeMock";
import Cart from "../src/components/Cart";
import { clearCart } from "../src/slice/cartSlice";

const mockStore = configureMockStore();

test("should clear cart when click on clear cart in cart component", () => {
  let store = mockStore(mockStoreInfo);

  const { rerender } = render(
    <Provider store={store}>
      <Cart />
    </Provider>
  );

  const clearCartButton = screen.getByRole("button", { name: "Clear Cart" });
  expect(clearCartButton).toBeInTheDocument();

  // 派发action并手动更新store状态
  store.dispatch(clearCart());
  store = mockStore({ cart: { items: [] } });
  rerender(
    <Provider store={store}>
      <Cart />
    </Provider>
  );

  expect(screen.getByText(" Your cart is empty !")).toBeInTheDocument();
});

额外优化建议

  • 给关键元素添加data-testid(如清空按钮data-testid="clear-cart"、空购物车提示data-testid="empty-cart-message"),使用screen.getByTestId让测试更稳定
  • 移除测试中的await,Redux状态更新是同步操作,无需等待

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 12:52:34