Testing components that are connected with Redux is a little tricker as you
cannot simply mount the component with enzyme until you have mocked the store,
otherwise errors will occur. In order to make this process simple we can use an
open source packages called redux-mock-store.
Let's get started by installing this to our development dependencies.
yarn add --dev redux-mock-store
No configuration is necessary so we can jump right back into the Map spec and
begin creating a mock store.
We will add a new top-level describe context to the spec which will concern
the connected version of the Map. This means we will also have to import the
ConnectedMap component.
// app/js/components/Map/__specs__/Map.spec.jsx
import React from 'react';
import { shallow } from 'enzyme';
import { pointsMock } from '../../../spec-helper';
- import { Map } from '../';
+ import ConnectedMap, { Map } from '../';
describe('Map component', () => {
describe('when there are no points', () => {
it('matches the snapshot', () => {
const wrapper = shallow(<Map points={[]} />);
expect(wrapper).toMatchSnapshot();
});
});
describe('when there are points', () => {
it('matches the snapshot', () => {
const wrapper = shallow(<Map points={pointsMock} />);
expect(wrapper).toMatchSnapshot();
});
});
});
+
+ describe('ConnectedMap component', () => {});
We can now import the configureStore function from redux-mock-store. We call
this function and pass it Redux a list of middleware libraries that our
application uses. Since we do not have any custom middleware in this application
we will pass an empty array to configureStore. This function returns a new
function which we will name mockStore that allows us to build a mocked Redux
store. We can pass any data we want to the mockStore function and it will be
used as the store state. The returned value from mockStore is the Redux store
object which we must give to the connected component we want to test as a prop.
We will pass the pointsMock mock data as our store value. We must however pass
an object { points: pointsMock } because we are telling the mock store that
the pointsMock array is the state of the points reducer. Basically, the keys
of the object you pass must match the reducer names that were defined in your
combineReducers call inside of app/js/reducers/index.js.
The final step, as with the unconnected components, is to mount the component
using shallow and tell Jest to expect the mounted component matches the
snapshot.
// app/js/components/Map/__specs__/Map.spec.jsx
import React from 'react';
import { shallow } from 'enzyme';
+ import configureStore from 'redux-mock-store';
describe('ConnectedMap component', () => {
+ const mockStore = configureStore([]);
+ const store = mockStore({ points: pointsMock });
+
+ it('maps store state to the props', () => {
+ const wrapper = shallow(<ConnectedMap store={store} />);
+
+ expect(wrapper).toMatchSnapshot();
+ });
});
If you run the tests with yarn test in your terminal you should see all tests
passing and a new snapshot for the connected component has been written.
Take a look at the
app/js/components/Map/__specs__/__snapshots__/Map.spec.jsx.snap file and you
will see the following snapshot has been written.
exports[`ConnectedMap component maps store state to the props 1`] = `
<Map
dispatch={[Function]}
points={
Array [
Object {
"details": Object {
"house": "Night's Watch",
"name": "The Wall",
"words": "Night gathers, and now my watch begins.",
},
"favourite": true,
"id": 1,
"x": 450,
"y": 110,
},
Object {
"details": Object {
"house": "Stark",
"name": "Winterfell",
"words": "Winter is Coming",
},
"favourite": false,
"id": 2,
"x": 375,
"y": 355,
},
]
}
store={
Object {
"clearActions": [Function],
"dispatch": [Function],
"getActions": [Function],
"getState": [Function],
"replaceReducer": [Function],
"subscribe": [Function],
}
}
storeSubscription={
Subscription {
"listeners": Object {
"clear": [Function],
"get": [Function],
"notify": [Function],
"subscribe": [Function],
},
"onStateChange": [Function],
"parentSub": undefined,
"store": Object {
"clearActions": [Function],
"dispatch": [Function],
"getActions": [Function],
"getState": [Function],
"replaceReducer": [Function],
"subscribe": [Function],
},
"unsubscribe": [Function],
}
}
/>
`;
Although we don't see the actual Pointer instances that the Map would
generate with this data as we can see in the unconnected snapshots, we do see
what information Redux is passing to the Map. The store,
storeSubscription, and dispatch props can be ignored as they are internals
of react-redux but we do see the points prop that Redux will automatically
pass into the Map component is the mock data that we expected from the store.
This tells us the mapStateToProps function from the Map is working
correctly.
We now know how to test connected and unconnected components. We will continue
in the next steps to cover the rest of our components, starting with the App.