Как выполнить модульное тестирование вывода метода компонента React в приложении React Router / Redux с помощью Enzyme - PullRequest
0 голосов
/ 01 марта 2019

У меня есть «контейнерный компонент», DashboardContainer, который подключен к хранилищу с избыточностью.

На один уровень выше в index.js, поставщик магазина переносит компонент 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>
);

Итак, вкратце, метод, который я пытаюсь протестировать, расположен в оболочках редукса и маршрутизатора.

Вот компонент с методом, который я пытаюсь проверить:

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

Несколько замечаний:

  1. IЯ не пытаюсь протестировать React Router или Redux.
  2. Я не использую {withRouter} в каких-либо HOC.
  3. Я не пытаюсь проверить, был ли вызван или вызван метод.
  4. Все Я пытаюсь выяснить, возвращает ли метод правильное значение, учитывая набор данных через реквизиты, которые предоставляются внутри теста.
  5. Я прочитал несколько постов здесь и в репозитории Github для Enzyme (например, link ) и считаю, что мне нужно использовать dive ().
  6. DashboardContainer на самом деле ничего не отображает, кромеэто дети.Он выполняет расчеты для данных, полученных из хранилища избыточных данных, и передает эти обработанные данные дочерним «презентационным» компонентам для визуализации.
  7. Тестирование в дочерних компонентах не поможет, так как они получают вычисленные значения.значения как реквизиты, которые правильно отображают.

Вот тест, с которым я сражаюсь:

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 () получит "undefined".

Если япопробуйте использовать mount вместо мелкого, все разрывы ада теряются, поскольку я не реализовал фиктивное хранилище и т. д.

ВОПРОС : если не считать копирования метода непосредственно впроверить (и сделать его функцией), как правильно нацелиться на метод, подобный этому, чтобы иметь возможность запуститьБыть против?Это где нырять?Или я пропустил какой-то фундаментальный аспект всего этого?

Ответы [ 2 ]

0 голосов
/ 02 марта 2019

Учитывая, что ваша цель состоит в модульном тесте методе getExerciseCalsForDay и НЕ в Redux или React маршрутизаторе, я настоятельно рекомендую извлечь логику из 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);

И 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);
});
0 голосов
/ 02 марта 2019

Документ Redux о написании тестов рекомендует следующее:

Чтобы иметь возможность тестировать сам компонент приложения, не обращаясь к декоратору, мыРекомендуется также экспортировать недекорированный компонент.

Экспортируйте подключенный компонент как default экспорт для использования в приложении, а сам компонент как с именем export для тестирования.:

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

export default connect(mapStateToProps)(DashboardContainer);  // default export

Затем импортируйте именованный экспорт (сам компонент) в тесте:

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

Это значительно упрощает тестирование сам компонент в ваших модульных тестах, и в этом случае, похоже, это единственное изменение, необходимое для работы вашего теста.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...