I've got a "container component", DashboardContainer, that is connected to a redux store.
One level up in index.js, the store Provider wraps an AppRouter component, like this:
<Provider store={store}>
<AppRouter />
</Provider>
The AppRouter setup is like this:
const AppRouter = () => (
<Router history={history}>
<Switch>
<PublicRoute exact path={ROUTES.LANDING} component={SignUpPage} />
<PrivateRoute path={ROUTES.DASHBOARD} component={DashboardContainer} />
<Route component={NotFoundPage} />
</Switch>
</Router>
);
So, in brief, the method I'm trying to test is nestled down under the redux and router wrappers.
Here's the component with the method, I'm trying to test:
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);
A few of notes:
- I'm not trying to test React Router or Redux.
- I'm not using {withRouter} in any HOCs.
- I'm not trying to test if or how the method has been called.
- All I'm trying to do is see if the method returns the correct value, given a data set via props, which are provided inside the test.
- I've read several posts here and on the Github repo for Enzyme (e.g link), and believe I need to use dive().
- The DashboardContainer doesn't actually render anything except for it's children. It performs calculations on data received from the redux store, and passes this processed data down to the child "presentational" components for rendering there.
- Testing in the child components won't help, as they are receiving the calculated values as props, which render correctly.
Here's the test I'm battling:
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);
});
The result of this this test is:
TypeError: instance.getExerciseCalsForDay is not a function
If I change the definition of instance to:
const instance = wrapper.instance().dive();
I get:
TypeError: wrapper.instance(...).dive is not a function
If I change the instance to:
const instance = wrapper.dive().instance();
I get:
TypeError: ShallowWrapper::dive() can only be called on components
If I try to run the except with this:
expect(instance.getExerciseCalsForDay).toBe(1501);
toBe() receives "undefined".
If I try to try to use mount, instead of shallow, all hell breaks lose, as I've not implemented a mock store, etc.
QUESTION: Short of copying the method directly into the test (and making it a function), how does one properly target a method like this so as to be able to run an expect/toBe against it? Is it where to dive? Or have I missed some fundamental aspect of this whole thing?