Reactjs 如何使用酶对React Router/Redux应用程序中React组件方法的输出进行单元测试

Reactjs 如何使用酶对React Router/Redux应用程序中React组件方法的输出进行单元测试,reactjs,redux,react-router,jestjs,enzyme,Reactjs,Redux,React Router,Jestjs,Enzyme,我有一个容器组件DashboardContainer,它连接到redux存储 在index.js的上一层,存储提供者包装了一个AppRouter组件,如下所示: <Provider store={store}> <AppRouter /> </Provider> const AppRouter = () => ( <Router history={history}> <Switch> <Publ

我有一个容器组件DashboardContainer,它连接到redux存储

在index.js的上一层,存储提供者包装了一个AppRouter组件,如下所示:

<Provider store={store}>
  <AppRouter />
</Provider>
const AppRouter = () => (
  <Router history={history}>
    <Switch>
      <PublicRoute exact path={ROUTES.LANDING} component={SignUpPage} />
      <PrivateRoute path={ROUTES.DASHBOARD} component={DashboardContainer} />
      <Route component={NotFoundPage} />
    </Switch>
  </Router>
);
如果我将实例的定义更改为:

const instance = wrapper.instance().dive();
const instance = wrapper.dive().instance();
我得到:

TypeError: wrapper.instance(...).dive is not a function
TypeError: ShallowWrapper::dive() can only be called on components
如果我将实例更改为:

const instance = wrapper.instance().dive();
const instance = wrapper.dive().instance();
我得到:

TypeError: wrapper.instance(...).dive is not a function
TypeError: ShallowWrapper::dive() can only be called on components
如果我尝试使用以下选项运行,则除外:

expect(instance.getExerciseCalsForDay).toBe(1501);
托比没有定义

如果我尝试使用mount,而不是shallow,那么所有的地狱突破都会失败,因为我没有实现模拟存储等等

问题:除了将方法直接复制到测试中并使其成为函数之外,如何正确地针对这样的方法,以便能够针对它运行expect/toBe?在哪里潜水?还是我遗漏了整个事件的一些基本方面?

Redux建议如下:

为了能够在不必处理装饰程序的情况下测试应用程序组件本身,我们建议您也导出未装饰的组件

将连接的组件导出为默认导出以在应用程序中使用,并将组件本身导出为命名导出以进行测试:

export class DashboardContainer extends Component {  // named export
  ...
}

export default connect(mapStateToProps)(DashboardContainer);  // default export
然后导入命名组件并在测试中导出组件本身:

...
import { DashboardContainer } from "../../../components/Dashboard/DashboardContainer";
...
这使得在单元测试中测试组件本身更加容易,在这种情况下,这似乎是测试工作所需的唯一更改。

Redux建议如下:

为了能够在不必处理装饰程序的情况下测试应用程序组件本身,我们建议您也导出未装饰的组件

将连接的组件导出为默认导出以在应用程序中使用,并将组件本身导出为命名导出以进行测试:

export class DashboardContainer extends Component {  // named export
  ...
}

export default connect(mapStateToProps)(DashboardContainer);  // default export
然后导入命名组件并在测试中导出组件本身:

...
import { DashboardContainer } from "../../../components/Dashboard/DashboardContainer";
...

这使得在单元测试中测试组件本身更加容易,在这种情况下,这似乎是测试工作所需的唯一更改。

鉴于您的目标是对getExerciseCalsForDay方法进行单元测试,而不是重复使用或反应路由器,我强烈建议将getExerciseCalsForDay内部的逻辑提取到纯JavaScript函数中

一旦它被提取出来,你就可以自己测试它,而无需通过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);
您的测试非常简单:

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);
});

考虑到您的目标是对getExerciseCalsForDay方法进行单元测试,而不是重复使用或响应路由器,我强烈建议将getExerciseCalsForDay中的逻辑提取到纯JavaScript函数中

一旦它被提取出来,你就可以自己测试它,而无需通过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);
您的测试非常简单:

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);
});

嗨,菲托,谢谢你的精彩、清晰的回答。我一直想把这些功能作为独立的功能,而你的回答正是我这样做的动力。这样做还暴露了一些以前似乎忽略的缺陷,例如没有为来自state的值设置默认值,这会在过滤器、映射或任何需要处理数组的东西时导致崩溃。无论如何,我感谢你在这方面的帮助。太棒了,我很高兴你发现我的建议很有帮助。菲托,谢谢你的回答。我一直想把这些功能作为独立的功能,而你的回答正是我这样做的动力。这样做还暴露了一些以前似乎忽略的缺陷,例如没有为来自state的值设置默认值,这会在过滤器、映射或任何需要处理数组的东西时导致崩溃。无论如何,我感谢你在这方面的帮助。太棒了,我很高兴你发现我的建议很有用。嗨,布莱恩。感谢您如此周到、富有创意的回应。而我选择将函数移出到它们自己的函数中。我认为这个解决方案在某些情况下可能会派上用场,我会把这个新技巧放在我的工具包里。我很感谢您在这里分享您的智慧。@Kim,我更新了我的答案,以供参考,并将其添加到Redux文档中,作为编写连接组件测试的推荐方法。谢谢。是的,在写这篇文章的时候,我找到了那篇文章,当我找到的时候,我想,布赖恩就是这么说的!。因此,是的,导出类/函数时同时使用和不使用Redux允许从两个角度进行测试,这非常有用。我很感激你的跟进。@Kim真棒,很高兴听到这对你有帮助!嗨,布莱恩。感谢您如此周到、富有创意的回应。而我选择将函数移出到它们自己的函数中。我认为这个解决方案在某些情况下可能会派上用场,我会把这个新技巧放在我的工具包里。我感谢你的支持
在这里分享您的智慧。@Kim,我用Redux文档中的a更新了我的答案作为参考,其中给出了为连接组件编写测试的推荐方法。谢谢。是的,在写这篇文章的时候,我找到了那篇文章,当我找到的时候,我想,布赖恩就是这么说的!。因此,是的,导出类/函数时同时使用和不使用Redux允许从两个角度进行测试,这非常有用。我很感激你的跟进。@Kim真棒,很高兴听到这对你有帮助!