Reputation: 31
I have a problem with my unit tests in jest/enzyme in my ReactJS project, I converted my components to ES6 and to use hooks for my redux connection to the reducer store. However after adding the change to using hooks in this component the unit test for it has been broke and I have spent a few days trying to figure out why this test will no longer shallow render as a full mount will work fine.
DemoPage.js
import React from 'react';
import { useSelector, useDispatch} from 'react-redux';
import * as actions from '../../actions/demoPageActions';
import DemoPageForm from '../demoApp/DemoPageForm';
import {compose} from "recompose";
import {withStyles} from "@material-ui/core";
const styles = theme => ({});
export const DemoPage = () => {
const demoState = useSelector(state => state.demoPage);
const dispatch = useDispatch();
const saveSomething = () => {
dispatch(actions.saveSomething(demoState));
};
const calculateSomething = e => {
dispatch(actions.calculateSomething(demoState, e.target.name, e.target.value));
};
return (
<DemoPageForm
onSaveClick={saveSomething}
onChange={calculateSomething}
demoState={demoState}
/>
);
};
export default compose(withStyles(styles))(DemoPage);
DemoPage.spec.js
import React from "react";
import { shallow } from "enzyme";
import {DemoPage} from "./DemoPage";
import DemoPageForm from "../demoApp/DemoPageForm";
describe("<DemoPage />", () => {
it("should contain <DemoPageForm />", () => {
const wrapper = shallow(
<DemoPage/>
);
expect(wrapper.find(DemoPageForm).length).toEqual(1);
});
});
Which produces the following error
<DemoPage /> › should contain <DemoPageForm />
Invariant Violation: could not find react-redux context value; please ensure the component is wrapped in a <Provider>
9 |
10 | export const DemoPage = () => {
> 11 | const demoState = useSelector(state => state.demoPage);
| ^
12 | const dispatch = useDispatch();
13 |
14 | const saveSomething = () => {
This is highly confusing as it should still be testing the unconnected component however I believe the hooks for useSelector are causing this issue so after reading about 30-40 pages worth of content about these I have yet to find a soltuion and the closest I have got is using mount which works fine however I would prefer shallow for this testing, here is the code and result when wrapped in a provider with a mockstore
describe("<DemoPage />", () => {
const mockStore = configureMockStore()
const store = mockStore(returnInitialState());
it("should contain <DemoPageForm />", () => {
const wrapper = shallow(
<Provider store={store}>
<DemoPage
store={store}
/>
</Provider>
);
console.log(wrapper.dive().debug())
expect(wrapper.find(DemoPageForm).length).toEqual(1);
});
});
console.log src/components/containers/DemoPage.spec.js:23
<DemoPage store={{...}} />
● <DemoPage /> › should contain <DemoPageForm />
expect(received).toEqual(expected) // deep equality
Expected: 1
Received: 0
23 | console.log(wrapper.dive().debug())
24 |
> 25 | expect(wrapper.find(DemoPageForm).length).toEqual(1);
| ^
26 | });
27 | });
If anyone knows how this can be solved it would be extremely helpful. Cheers
Upvotes: 3
Views: 1518
Reputation: 132
I've had problems working with shallow
rendering with enzyme. Currently, enzyme does not support react hooks fully in shallow rendered test components. You will need to use mount for the moment. You can track progress on the issue page tied to their github in issue #1938
If you want to try using shallow, you do have access to quite a bit of the functionality currently. Try upgrading enzyme-adapter-react-16
to version 1.15.1
or newer to eliminate some of the more glaring issues. It definitely still has issues as of 11/12/19 when I last tried to add it in, but it's getting better all the time as they work on the compatibility issues.
Upvotes: 2
Reputation: 19977
Your test fails precisely because shallow
just renders one level of the DOM tree. Thus you only get the <Provider />
part of the tree, never reaches <DemoPage />
. That also explains why mount
works fine.
You probably should read the doc™️ more carefully before you google frenzy mode. Note that shallow(node, options)
function accepts a second argument. And there it provides two ways to interact with React Context:
options.context
directlyoptions.wrappingComponent
.Example code to options.wrappingComponent
usage:
import { Provider } from 'react-redux';
import { Router } from 'react-router';
import store from './my/app/store';
import mockStore from './my/app/mockStore';
function MyProvider(props) {
const { children, customStore } = props;
return (
<Provider store={customStore || store}>
<Router>
{children}
</Router>
</Provider>
);
}
MyProvider.propTypes = {
children: PropTypes.node,
customStore: PropTypes.shape({}),
};
MyProvider.defaultProps = {
children: null,
customStore: null,
};
const wrapper = shallow(<MyComponent />, {
wrappingComponent: MyProvider,
});
const provider = wrapper.getWrappingComponent();
provider.setProps({ customStore: mockStore });
Disclaimer: above code is shamelessly copy-pasted directly from the enzyme doc link here.
Upvotes: 0