我有一个“容器组件”,DashboardContainer,它连接到redux商店。
在index.js中上一层,商店Provider包装了一个AppRouter组件,如下所示:
<Provider store={store}>
<AppRouter />
</Provider>
AppRouter的设置如下:
const AppRouter = () => (
<Router history={history}>
<Switch>
<PublicRoute exact path={ROUTES.LANDING} component={SignUpPage} />
<PrivateRoute path={ROUTES.DASHBOARD} component={DashboardContainer} />
<Route component={NotFoundPage} />
</Switch>
</Router>
);
因此,简单来说,我要测试的方法位于redux和router包装器下。
这是该方法的组成部分,我正在尝试测试:
import React, { Component } from "react";
import { connect } from "react-redux";
import Dashboard from "./Dashboard";
import PropTypes from "prop-types";
import moment from "moment";
class DashboardContainer extends Component {
static propTypes = {
dashboardDate: PropTypes.string.isRequired,
exerciseLog: PropTypes.array.isRequired
};
componentDidMount() {
this.props.setDashboardDate(moment().format());
}
getExerciseCalsForDay = () => {
const { dashboardDate, exerciseLog } = this.props;
const totalCals = exerciseLog
.filter(entry => {
return moment(dashboardDate).isSame(entry.date, "day");
})
.map(entry => {
return entry.workouts
.map(item => item.nf_calories || 0)
.reduce((acc, curr) => acc + curr, 0);
});
return totalCals[0] || 0;
};
render() {
return (
<Dashboard
exerciseCalsToday={this.getExerciseCalsForDay()}
exerciseLog={this.props.exerciseLog}
/>
);
}
}
const mapStateToProps = state => ({
dashboardDate: state.dashboard.dashboardDate,
exerciseLog: state.exerciseLog
});
export default connect(mapStateToProps)(DashboardContainer);
一些注意事项:
这是我要进行的测试:
import React from "react";
import { shallow } from "enzyme";
import DashboardContainer from "../../../components/Dashboard/DashboardContainer";
import data from "../../fixtures/ExerciseLogSeedData";
const props = {
dashboardDate: "2019-03-01T19:07:17+07:00",
foodLog: data
};
const wrapper = shallow(<DashboardContainer {...props} />);
const instance = wrapper.instance();
test("should correctly calculate exercise calories for the day", () => {
expect(instance.getExerciseCalsForDay()).toBe(1501);
});
此测试的结果是:
TypeError: instance.getExerciseCalsForDay is not a function
如果我将实例的定义更改为:
const instance = wrapper.instance().dive();
我得到:
TypeError: wrapper.instance(...).dive is not a function
如果我将实例更改为:
const instance = wrapper.dive().instance();
我得到:
TypeError: ShallowWrapper::dive() can only be called on components
如果我尝试与此运行例外,则:
expect(instance.getExerciseCalsForDay).toBe(1501);
toBe()收到“未定义”。
如果我尝试使用mount而不是浅表,那么所有地狱都将丢失,因为我没有实现模拟存储,等等。
问题:缺少将方法直接复制到测试中(并使其成为函数)的方法,如何正确地针对这样的方法,以便能够针对它?在哪里潜水?还是我错过了这件事的一些基本方面?
答案 0 :(得分:2)
Redux
doc on writing tests建议以下内容:
为了能够测试App组件本身而不必处理装饰器,我们建议您还导出未装饰的组件。
将连接的组件导出为default
导出以在应用程序中使用,并将组件本身导出为名为命名的导出进行测试:
export class DashboardContainer extends Component { // named export
...
}
export default connect(mapStateToProps)(DashboardContainer); // default export
然后在测试中导入以命名的导出(组件本身):
...
import { DashboardContainer } from "../../../components/Dashboard/DashboardContainer";
...
这使在单元测试中测试组件本身容易得多,在这种情况下,这似乎是测试工作所需的唯一更改。
答案 1 :(得分:1)
鉴于您的目标是对getExerciseCalsForDay
方法进行单元测试,而不要使用Redux或React路由器,我强烈建议将getExerciseCalsForDay
中的逻辑提取到纯JavaScript函数中< / p>
一旦将其提取出来,您就可以自己对其进行测试,而无需通过React。
然后,您可以将getExerciseCalsForDay
导入到组件的index.js
文件中,并从组件的方法中调用它:
import React, { Component } from "react";
import { connect } from "react-redux";
import Dashboard from "./Dashboard";
import PropTypes from "prop-types";
import moment from "moment";
import calculateExerciseCalsForDay from "calculateExerciseCalsForDay";
class DashboardContainer extends Component {
static propTypes = {
dashboardDate: PropTypes.string.isRequired,
exerciseLog: PropTypes.array.isRequired
};
componentDidMount() {
this.props.setDashboardDate(moment().format());
}
getExerciseCalsForDay = () => {
const { dashboardDate, exerciseLog } = this.props;
return calculateExerciseCalsForDay(dashboardDate, exerciseLog);
};
render() {
return (
<Dashboard
exerciseCalsToday={this.getExerciseCalsForDay()}
exerciseLog={this.props.exerciseLog}
/>
);
}
}
const mapStateToProps = state => ({
dashboardDate: state.dashboard.dashboardDate,
exerciseLog: state.exerciseLog
});
export default connect(mapStateToProps)(DashboardContainer);
calculateExerciseCalsForDay.js
将包含:
export default function calculateExerciseCalsForDay(date, exerciseLog) {
const totalCals = exerciseLog
.filter(entry => {
return moment(date).isSame(entry.date, "day");
})
.map(entry => {
return entry.workouts
.map(item => item.nf_calories || 0)
.reduce((acc, curr) => acc + curr, 0);
});
return totalCals[0] || 0;
}
您的测试非常简单:
import calculateExerciseCalsForDay from "calculateExerciseCalsForDay";
import data from "../../fixtures/ExerciseLogSeedData";
const dashboardDate = "2019-03-01T19:07:17+07:00";
const foodLog = data;
};
test("should correctly calculate exercise calories for the day", () => {
expect(
calculateExerciseCalsForDay(dashboardDate, foodLog)
).toBe(1501);
});