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中
clearCartreducer的日志未输出,说明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
相关产品推荐
相关产品推荐

