Невозможно отправить действие в Jest

У нас есть компонент корзины в приложении React:

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.info("Here in the handle clear cart onClick event handler");
    console.info("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;

Это CartSlice, который мы используем:

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.info("Here in the clear cart reducer");
      state.items.length = 0;
    },
  },
});

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

В пользовательском интерфейсе, когда мы нажимаем кнопку «Очистить корзину», она очищается и отображается сообщение «Ваша корзина пуста».

Мы пытаемся написать модульный тест для него в библиотеке тестирования Jest/React. Вот наша попытка:

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.info("clearCart button is", clearCartButton);
  fireEvent.click(clearCartButton);
  expect(await (screen.getByText(" Your cart is empty !"))).toBeInTheDocument();
});

Судя по тому, что мы видим, кнопка clear Cart регистрируется и является действительной кнопкой. Также вызывается функция handleClearCart, и мы также можем распечатать тело функции clearCart. Однако мы не видим журнал консоли действия clearCart в CartSlice, что указывает на то, что оно не вызывается и, следовательно, тест на утверждение Your cart is empty ! не пройден. Мы не уверены, почему действие не отправляется при нажатии этой кнопки. Может ли кто-нибудь нам помочь?

PS: Структура 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>

🤔 А знаете ли вы, что...
React - это библиотека JavaScript, разработанная компанией Facebook.


2
56
1

Ответ:

Решено

redux-mock-store устарело; на самом деле он не управляет никакими состояниями или действиями, отправленными процессом. Сейчас общепринятой рекомендацией и практикой является создание настоящего хранилища Redux при модульном тестировании.

Пример:

import React from 'react';
import { fireEvent, render, screen, waitFor } from "@testing-library/react";
import "@testing-library/jest-dom";
import { configureStore } from "@reduxjs/toolkit"; // <-- create real store
import { Provider } from 'react-redux';
import reducer from '../path/to/rootReducer'; // <-- import reducer
import Cart from "../src/components/Cart";

const preloadedState = {
  // any initial state necessary for the unit tests
};

const store = configureStore({
  preloadedState,
  reducer,
});

const Providers = ({ children }) => (
  <Provider store = {store}>
    {children}
  </Provider>
);

test(
  "should clear cart when click on clear cart in cart component",
  async () => {
    render(<Cart />, { wrapper: Providers });

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

    console.info("clearCart button is", clearCartButton);

    fireEvent.click(clearCartButton);

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

Дополнительную общую информацию и стратегии тестирования см. в документации Redux Написание тестов.