我有一个纯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组件的原因。 你能建议为什么孩子们的身体不健康吗? 我以前看过这个,即使我尝试过Enzyme我也遇到了这个问题。 由于它是Redux包装的组件,因此我无法直接创建Landing Page或Tile组件进行测试。 如何测试嵌套项目?
答案 0 :(得分: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()
这将给您所有已分派的动作。
如果要根据商店数据测试应用程序的呈现方式,则可以:
const existingModules = [ ... ]; // list of modules
store = mockStore({
data: {
modules: existingModules,
cart: [],
total: 0
}
});
const existingModules = [ ... ]; // list of modules
const spy = jest.spyOn(redux, 'useSelector')
spy.mockReturnValue(existingModules)