React container testing example

Testing React containers is usually problematic, because then component is made depending on Redux state structure. Rendering component makes it automatically fail without right environment. Component can be tested by exporting it before binding, but then it's only partially tested.

Here's a tutorial for simple test set for containers.

Needed libraries

This test environment has build on Create React App. This list is bare minimum installed on it for getting tests running.

  • enzyme
  • redux-test-utils
  • react-redux
  • redux

We are using Jest from Create React App package as our test framework.

Components to test

Here are two components. Presentational component (Button) and container component (LogOutButtonContainer) which is going to be tested.

/src/components/Button.js

import React from 'react';

const Button = props => <button {...props} />;
export default Button;

/src/containers/LogOutButtonContainer.js

import React from 'react';
import { connect } from 'react-redux';
import Button from '../components/Button';
import { logout } from '../actions/session'

const LogOutButtonContainer = props => (
  <Button onClick={props.logout}>
    {props.logoutText}
  </Button>
);

const mapStateToProps = state => ({
  logoutText: state.session.logoutText
});

const mapDispatchToProps = {
  logout
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(LogOutButtonContainer);

Preparations

To help testing, here's test helper to render components. It attaches mocked store to given component.

/src/test-utils/shallowWithStore.js

import { shallow } from 'enzyme';

/*
 * Component wrapper for containers when wanted to test with Redux connect.
 * Example: const component = shallowWithStore(<ConnectedShowBox />, store);
 *
 * @param {Object} component - React component to be wrapped.
 * @param {Object} store - Store made with createMockStore from redux-test-utils module.
 */
const shallowWithStore = (component, store) => {
  const context = {
    store
  };
  return shallow(component, { context });
};

export default shallowWithStore;

Testing

Here's smoke test with bare minimum. Variable mockState contains needed structure of state. In this example mockState is defined under describe so it's reusable.

/src/containers/LogOutButtonContainer.test.js

import React from "react";
import { createMockStore } from "redux-test-utils";
import shallowWithStore from "../test-utils/shallowWithStore";
import LogOutButtonContainer from "./LogOutButtonContainer";

describe("LogOutButtonContainer", () => {
  const mockState = {
    session: {
      logoutText: "Log me out"
    }
  };

  it("renders", () => {
    const store = createMockStore(mockState);
    const container = shallowWithStore(<LogOutButtonContainer />, store);
    expect(container).toBeTruthy();
  });
});

And when it's wanted to test action dispatching, we can event test it with simulating clicks. When using this wrapper, use dive before entering the rendered component.

import React from "react";
import { createMockStore } from "redux-test-utils";
import shallowWithStore from "../test-utils/shallowWithStore";
import LogOutButtonContainer from "./LogOutButtonContainer";
import Button from '../components/Button';
import { logout } from '../actions/session'

describe("LogOutButtonContainer", () => {
  const mockState = {
    session: {
      logoutText: "Log me out"
    }
  };

  it("dispatches action on click", () => {
    const store = createMockStore(mockState);

    shallowWithStore(<LogOutButtonContainer />, store)
      .dive()
      .find(Button)
      .simulate("click");

    expect(store.isActionDispatched(logout())).toEqual(true);
  });
});

Code example

Here is a code example to check out: Github: Scionar/react-container-testing-example