2

我有一个纯 React-Redux 应用程序,它按预期工作。

App.js _

import React, { useEffect } from "react";
import { useDispatch } from "react-redux";
import { Router, Route, Switch, Redirect } from "react-router-dom";

import history from "../history";
import LandingPage from "./home/LandingPage";
import { displayModules } from "../actions";
import Cart from "./home/Cart";

const App = () => {
  const dispatch = useDispatch();
  useEffect(() => {
    dispatch(displayModules());
  }, [dispatch]);

  return (
    <Router history={history}>
      <Switch>
        <Route path="/" exact component={LandingPage}></Route>
        <Route path="/cart" exact component={Cart}></Route>
        <Route render={() => <Redirect to="/" />} />
      </Switch>
    </Router>
  );
};

export default App;

LandingPage有一个名为 Tile 的嵌套组件。

import React from "react";
import { useSelector, useDispatch } from "react-redux";
import Tile from "../common/Tile";

import { addItemToCart, displayCartContents } from "../../actions";
import "./LandingPage.css";

const LandingPage = () => {
  const modules = useSelector(state => state.data.modules);
  const cart = useSelector(state => state.data.cart);
  const dispatch = useDispatch();
  const addToCart = item => {
    dispatch(addItemToCart(item));
  };
  return (
    <div className="app">
      <div className="header">
        <div className="text">Insurance modules</div>
        <i
          className="shopping cart icon"
          onClick={() => {
            dispatch(displayCartContents());
          }}
        >
          <span className="badge">{cart.length}</span>
        </i>
      </div>
      <div className="body">
        {modules.map(module => (
          <Tile key={module.id} module={module} addToCart={addToCart}></Tile>
        ))}
      </div>
    </div>
  );
};

export default LandingPage;

Tile.js有一个我想测试的按钮。

import React, { useState } from "react";

import "./Tile.css";

const Tile = props => {
  const { module, addToCart } = props;
  const [coverage, setCoverage] = useState(parseInt(module.coverageMax - module.coverageMin) / 2);
  const [price, setPrice] = useState((coverage * module.risk) / 100);
  return (
    <div className="tile">
      <div className="tile-description">
        <div>
          <i className={`${module.icon} icon`}></i>
        </div>
        <div className="tile-name">{module.name}</div>
        <div className="tile-risk">Risk(%): {module.risk}</div>
      </div>
      <div className="tile-footer">
        <div className="tile-range">
          <div className="field-label">
            Select Coverage: <span className="coverage-display">{coverage}</span>
          </div>
          <div className="slidecontainer">
            <span className="slider-step">{module.coverageMin}</span>
            <input
              type="range"
              min={module.coverageMin}
              max={module.coverageMax}
              value={coverage}
              className="slider"
              onChange={e => {
                setCoverage(e.target.value);
                setPrice((e.target.value * module.risk) / 100);
              }}
            ></input>
            <span className="slider-step">{module.coverageMax}</span>
          </div>
        </div>
        <div>
          PRICE at this Coverage:<span className="tile-price">{price}</span>
        </div>

        <button
          className="tile-button"
          onClick={() => {
            addToCart({
              id: module.id,
              name: module.name,
              coverage: coverage,
              price: price,
              timeStamp: Math.ceil(new Date().getTime() * Math.random() * Math.random())
            });
          }}
        >
          Add module to cart
        </button>
      </div>
    </div>
  );
};

export default Tile;

App.test.js工作正常,我可以通过 className 属性找到嵌套的着陆页 div。

import React from "react";
import configureStore from "redux-mock-store";
import { Provider } from "react-redux";
import renderer from "react-test-renderer";

import App from "../components/App";
import history from "../history";
import { displayModules } from "../actions";
import { DISPLAY_MODULES } from "../actions/types";

const mockStore = configureStore([]);

describe("App Component test", () => {
  let store = {};
  let wrappedComponent = {};
  const expectedActions = {
    type: DISPLAY_MODULES,
    payload: [
      {
        id: 0,
        icon: "bicycle",
        name: "Bike",
        coverageMin: 0,
        coverageMax: 3000,
        risk: 30
      },
      {
        id: 1,
        icon: "gem",
        name: "Jewelry",
        coverageMin: 500,
        coverageMax: 10000,
        risk: 5
      },
      {
        id: 2,
        icon: "microchip",
        name: "Electronics",
        coverageMin: 500,
        coverageMax: 6000,
        risk: 35
      },
      {
        id: 3,
        icon: "football ball",
        name: "Sports Equipment",
        coverageMin: 0,
        coverageMax: 20000,
        risk: 30
      }
    ]
  };
  beforeEach(() => {
    store = mockStore({
      data: {
        modules: [],
        cart: [],
        total: 0
      }
    });
    store.dispatch = jest.fn(displayModules);
    wrappedComponent = renderer.create(
      <Provider store={store}>
        <App />
      </Provider>
    );
  });

  it("should render with given state from Redux store", () => {
    expect(wrappedComponent.toJSON()).toMatchSnapshot();
  });
  it("should have an app from Landing Page", () => {
    expect(wrappedComponent.root.findByProps({ className: "app" })).toBeDefined();
  });

  it("should show landing page for default route", () => {
    *debugger;
    expect(wrappedComponent.root.findByProps({ className: "shopping cart icon" })).toBeDefined();*
  });
  it("should show cart page for /cart route", () => {
    history.push("/cart");
    expect(wrappedComponent.root.findByProps({ className: "backward icon" })).toBeDefined();
  });
  it("should redirect to landing page for unmatched 404 routes", () => {
    history.push("/someRandomRoute");
    expect(wrappedComponent.root.findByProps({ className: "shopping cart icon" })).toBeDefined();
  });
  it("should dispatch displayModules action on app mount", async () => {
    const actualAction = await store.dispatch();
    expect(actualAction).toEqual(expectedActions);
  });
});

但是如果你看到测试调试器 在此处输入图像描述

具有className: body的 div 的孩子没有孩子。这就是它无法找到 Tile 组件的原因。你能建议为什么孩子们对身体无效吗?我以前见过这个,即使我尝试使用酶我也遇到了这个问题。由于它是一个 Redux 包装的组件,我无法直接创建登陆页面或 Tile 组件进行测试。如何测试嵌套项目?

4

1 回答 1

4

您正在为 redux 状态中的模块提供一个空数组:

store = mockStore({
  data: {
    modules: [], // your modules is empty so no tiles will render
    cart: [],
    total: 0
  }
});

另一个问题是您模拟了 store.dispatch ,因此即使调度了某些操作,它也不再更改 redux 存储:

store.dispatch = jest.fn(displayModules);

如果你想测试一个动作是否被调度,你可以使用:

const actions = store.getActions()

这将为您提供所有已调度的操作。

如果您想根据您的商店数据测试您的应用程序如何呈现,您可以:

  1. 在测试中设置商店:
const existingModules = [ ... ]; // list of modules
store = mockStore({
  data: {
    modules: existingModules, 
    cart: [],
    total: 0
  }
});
  1. 您可以在测试中模拟 useSelector :
const existingModules = [ ... ]; // list of modules
const spy = jest.spyOn(redux, 'useSelector')
spy.mockReturnValue(existingModules)
于 2020-03-20T11:53:00.537 回答