Arthur Z
Arthur Z

Reputation: 170

How to test function that passed from mapDispatchToProps (React/Redux/Enzyme/Jest)

I want to test that function passed from mapDispatchToProps was invoked when button clicking is simulated.

How to test that function which passed from mapDispatchToProps is invoked?

I tried to pass a mocked function by props, but it doesn't work. Any help will be appreciated.

Here below my fake class code and test example.

My component

// All required imports

class App extends React.Component<Props> {
  render() {
    const { onClick } = this.props;
    return (
      <>
        <h1>Form</h1>
        <input />
        <button onClick={() => onClick()} />
      </>
    );
  }
}

const mapStateToProps = (state) => {
  return {
    state
  };
};

const mapDispatchToProps = (dispatch) => {
  return {
    onClick: () => dispatch(actions.onClick())
  };
};

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

My test file

import { configure, mount } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16/build/index';
import jsdom from 'jsdom';
import React from 'react';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import ConnectedApp, { App } from './App';

function setUpDomEnvironment() {
  const { JSDOM } = jsdom;
  const dom = new JSDOM('<!doctype html><html><body></body></html>', { url: 'http://localhost/' });
  const { window } = dom;
  global.window = window;
  global.document = window.document;
  global.navigator = {
    userAgent: 'node.js',
  };
  copyProps(window, global);
}

function copyProps(src, target) {
  const props = Object.getOwnPropertyNames(src)
    .filter(prop => typeof target[prop] === 'undefined')
    .map(prop => Object.getOwnPropertyDescriptor(src, prop));
  Object.defineProperties(target, props);
}

setUpDomEnvironment();

configure({ adapter: new Adapter() });


const mockStore = configureMockStore();

describe('App', () => {
  describe('When App connected to store', () => {
    describe('When button clicked', () => {
      it('should not crush after click on login button', () => {
      const onClick = jest.fn()
        const store = mockStore(initialStates[1]);
        const wrapper = mount(
          <Provider store={store}>
            <ConnectedApp />
          </Provider>);
        wrapper.find('button').simulate('click');
        ??? how to test that function passed from mapDispatchToProps was fired?
      });
    });
  });
});

Upvotes: 8

Views: 8391

Answers (1)

Brian Adams
Brian Adams

Reputation: 45810

I recommend following the approach described in the docs and export the connected component as the default export for use in the application, and export the component itself as a named export for testing.

For the code above export the App class and test the click like this:

import * as React from 'react';
import { shallow } from 'enzyme';
import { App } from './code';

describe('App', () => {

  it('should call props.onClick() when button is clicked', () => {
    const onClick = jest.fn();
    const wrapper = shallow(<App onClick={onClick} />);
    wrapper.find('button').simulate('click');
    expect(onClick).toHaveBeenCalledTimes(1);
  });

});

shallow provides everything that is needed for testing the component itself. (shallow even calls React lifecycle methods as of Enzyme v3)

As you have found, to do a full rendering of the component requires a mock redux store and wrapping the component in a Provider. Besides adding a lot of complexity, this approach also ends up testing the mock store and all child components during the component unit tests.

I have found it much more effective to directly test the component, and to export and directly test mapStateToProps() and mapDispatchToProps() which is very easy since they should be pure functions.

The mapDispatchToProps() in the code above can be tested like this:

describe('mapDispatchToProps', () => {
  it('should dispatch actions.onClick() when onClick() is called', () => {
    const dispatch = jest.fn();
    const props = mapDispatchToProps(dispatch);
    props.onClick();
    expect(dispatch).toHaveBeenCalledWith(actions.onClick());
  });
});

This approach makes unit testing the component very simple since you can pass the component props directly, and makes it very simple to test that the component will be handed the correct props by the pure functions (or objects) passed to connect().

This ensures that the unit tests are simple and targeted. Testing that connect() and redux are working properly with the component and all of its child components in a full DOM rendering can be done in the e2e tests.

Upvotes: 5

Related Questions