W. Shane Brunson

Unit Testing Redux Container Components - Part 1

May 08, 2016

Writing large React applications requires a sane solution to managing state. There are a few libraries to choose from, but the most popular is Redux. Redux has some great features, including a convenient way to create Container components that can map state and actions to the properties of a presentational component.

Redux also includes a component called Provider. Provider is used to give all of the components in an application access to the Redux store. Without Provider, a child component would have to have the Redux store passed to it by all of it’s parents to modify the store. This causes problems when unit testing Redux Containers though. A true unit test is isolated from the rest of the application, so the container component is being rendered outside of the Provider component, meaning it has lost access to the Redux store.

Let’s start with a simple presentational component. Our hypothetical app is a Quiz. Our component is a Choice, something that would render as a child of a Question. When clicking a Choice, it would add it’s question and choice Id into the selections state of the Redux store. Here is the Choice component:

{% highlight javascript %} import React from ‘react’;

const Choice = ({ saveSelection, choiceText }) => { return (

  • ); };

    export default Choice; {% endhighlight %}

    And the container component:

    {% highlight javascript %} import { connect } from ‘react-redux’; import { addSelection } from ‘../actions’;

    import Choice from ‘../components/Choice’;

    const mapStateToProps = () => { return { isSelected: false, }; };

    const mapDispatchToProps = (dispatch, ownProps) => { return { saveSelection: () => { dispatch(addSelection( ownProps.questionNumber, ownProps.choiceNumber )); }, }; };

    const ChoiceContainer = connect( mapStateToProps, mapDispatchToProps )(Choice);

    export default ChoiceContainer; {% endhighlight %}

    Note: in the saveSelection method, ownProps.questionNumber and ownProps.choiceNumber are integers being passed to ChoiceContainer from its parent component that is not shown in this post. We are not using them in the Choice component at this time, but they are important for identifying which Choice of which Question was selected.

    I have seen some developers combine the container and the presentation classes into one file and export the container component. I like splitting them into separate files, especially if mapDispatchToProps and mapStateToProps functions get any more complicated than the example above.

    Now that we have two components, let’s test them. I’m using Airbnb’s Enzyme library to compile the components. I would highly recommend using Enzyme. It makes rendering components and checking their structure, props, and state simple.

    First, the Choice component: {% highlight javascript %} import React from ‘react’; import { shallow } from ‘enzyme’;

    import Choice from ’../../src/js/components/Choice’;

    describe(‘Choice Component’, () => { const choiceText = ‘Choice 1’; const saveSelection = jasmine.createSpy(‘saveSelection’); let Component;

    beforeEach(() => {
    	Component = shallow(
    it('should render', () => {
    it('should call saveSelection when clicked', () => {

    }); {% endhighlight %}

    And now the ChoiceContainer component: {% highlight javascript %} import React from ‘react’; import { Provider } from ‘react-redux’; import { mount } from ‘enzyme’;

    import { storeFake } from ‘../fakeData/storeFake’; import ChoiceContainer from ’../../src/js/containers/ChoiceContainer’; import Choice from ’../../src/js/components/Choice’;

    describe(‘ChoiceContainer’, () => { let Component; let ChoiceComponent;

    beforeEach(() => {
    	const store = storeFake({});
    	const wrapper = mount(
    	Component = wrapper.find(ChoiceContainer);
    	ChoiceComponent = Component.find(Choice);
    it('should render', () => {

    }); {% endhighlight %}

    We are able to compile and find a ChoiceContainer component because of these lines: {% highlight javascript %} const store = storeFake({});

    const wrapper = mount( );

    Component = wrapper.find(ChoiceContainer); ChoiceComponent = Component.find(Choice); {% endhighlight %} We created a Provider component, gave it a fake store, and wrapped the ChoiceContainer with it. Then we used Enzyme’s find method to find the rendered ChoiceContainer and Choice components.

    Here is what the fake store looks like: {% highlight javascript %} export const storeFake = (state) => { return { default: () => {}, subscribe: () => {}, dispatch: () => {}, getState: () => { return { …state }; }, }; }; {% endhighlight %} Note: the Object spread operator, { ...state }, will not automatically compile with Babel. You will need to install this package from npm and update your Babel presets.

    Any object passed to the fake store will be used by the Provider component when rendering the ChoiceComponent.

    Our tests don’t do much at the moment, but this will at least allow them to compile. In the next post, I will demonstrate how I go about testing container components. If you’d like to see the project as a whole, check out the QuizSimply Github repository.