TheNovice
TheNovice

Reputation: 1287

Testing Connected Container methods in Jest + Enzyme

I have a connected container like so:

import React from 'react';
import PropTypes from 'prop-types';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import { autobind } from 'core-decorators';

const propTypes = {
  data: PropTypes.object,
  userData: PropTypes.object,
};

class ConnectedContainer extends React.Component {
    @autobind
    doSomethingImportant() {
      ...
    }

  render() {
    ....
  }
}

ConnectedContainer.propTypes = propTypes;

const mapStateToProps = state => ({ data, userData });
const mapDispatchToProps = (dispatch) => {
  return { actions: bindActionCreators(actions, dispatch) };
};
export default connect(mapStateToProps, mapDispatchToProps)(ConnectedContainer);

I want to test doSomethingImportant, so I have test like so:

import React from 'react';
import { shallow } from 'enzyme';
import ConnectedContainer from '.....'';
import configureStore from 'redux-mock-store';

const mockStore = configureStore();
const store = mockStore({ getState: () => null, dispatch: () => null, data: { data from fixture }, userData: { data from fixture } });
const container = (
  <ConnectedContainer
    store={store}
    actions={{}}
  />
);

describe('ConnectedContainer', () => {
  describe('doSomethingImportant', () => {
    it('returns something important', () => {
      const wrapper = shallow(container);
      expect(wrapper.instance().doSomethingImportant()).to.equal( ... some output here );
    });
  });
});

And no matter what I do, I get this error:

TypeError: wrapper.instance(...). doSomethingImportant is not a function

What is happening with my container that I'm unable to access its instance methods?

Upvotes: 3

Views: 2729

Answers (2)

stone
stone

Reputation: 8652

Test the unwrapped component

Export the ConnectedContainer class itself, not the wrapped version. You want to test your code, not the connect() function.

You can keep the default export as the wrapped component, and then add the word export in front of the class definition:

export class ConnectedContainer extends React.Component { // Named export, for testing purposes only
    ...
}

Then in your test, import { ConnectedContainer } from '....' Render that with shallow instead of the default export.

Naming conventions

Also, naming your component ConnectedContainer is very confusing! It only becomes connected after it is wrapped with the connect function. So when you export ConnectedContainer (as I've suggested) you're actually exporting a non-connected component class. (And is it actually a container? That's also pretty vague.)

One naming convention people use is to define a constant that holds the return value of connect(), and name that xxxContainer, like so:

export class IconView extends React.Component {  // Exported for testing
  // ... class implementation
}

function mapStateToProps(){...}
function mapDispatchToProps(){...}

const IconViewContainer = connect(mapStateToProps, mapDispatchToProps)(IconView);
export default IconViewContainer;  // Exported for use as a component in other views

Upvotes: 5

sebastianf182
sebastianf182

Reputation: 9978

Try:

const Container = (
  <ConnectedContainer
    store={store}
    actions={{}}
  />
);

Note, the only difference is the C uppercase in container. The thing is that React only treats classes and methods as components if they start with a capital letter. That could be your problem.

Upvotes: 0

Related Questions