I'm having trouble testing a very specific use case of my React components. In this case, I have a component that dispatches an action on mount, and then dispatches another action after the results of the first dispatch come in.
The problem, I believe, comes down to enzyme's shallow method not calling componentDidUpdate in my component. I have reproduced the problem with the simplest code I could, and confirmed that the problem disappears if I use mount instead of shallow. However, enzyme's documentation says that since version 3, shallow does call React's lifecycle methods such as componentDidUpdate. And of course, I cannot use mount in my test because I don't want to mount the entire tree (in my case, that would mean loading my entire application).
Thus I wonder: is this a bug in enzyme, or I am missing something in my usage of that library?
Here's my code example:
import * as React from 'react';
import { connect } from 'react-redux';
import { createStore } from 'redux';
import { shallow } from 'enzyme';
import sinon from 'sinon';
const actions = {
// This would fetch a resource on the network.
getList: () => {},
receiveList: list => {
return {
type: 'RECEIVE_LIST',
list,
};
},
// This would fetch a list of resources on the network.
getObjects: () => {},
};
const reducer = (state = { list: [] }, action) => {
switch (action.type) {
case 'RECEIVE_LIST':
return { ...state, list: action.list };
default:
return state;
}
};
class MyAppBase extends React.Component {
componentDidMount() {
this.props.dispatch(actions.getList());
}
componentDidUpdate(nextProps) {
if (nextProps.list !== this.props.list) {
this.props.dispatch(actions.getObjects(nextProps.list));
}
}
render() {
return null;
}
}
const MyApp = connect(state => { return { list: state.list }; })(MyAppBase);
describe('<MyApp>', () => {
it('fetches objects once list is received', () => {
// Mock actions that call the network.
sinon.stub(actions, 'getList');
actions.getList.returns({type: 'whatever'});
sinon.stub(actions, 'getObjects');
actions.getObjects.returns({type: 'whatever'});
// Create a redux store.
const store = createStore(reducer);
// Create the component.
const wrapper = shallow(<MyApp store={store} />);
// Verify that `componentDidMount` was called but not `componentDidUpdate`.
expect(actions.getList.callCount).toEqual(1);
expect(actions.getObjects.callCount).toEqual(0);
// Dispatch an action that will change the redux store.
store.dispatch(actions.receiveList([ 'aa', 'bb' ]));
// Expect the `componentDidUpdate` method to be called.
expect(actions.getObjects.callCount).toEqual(1);
// This doesn't work, for some reason `componentDidUpdate` is never called.
});
});
Package versions I use for this:
"react": "16.6.0",
"react-dom": "16.6.0",
"react-redux": "5.1.0",
"redux": "4.0.1",
"enzyme": "3.7.0",
"enzyme-adapter-react-16": "1.6.0",
"sinon": "7.1.0"
Thanks!