enzyme: TypeError: Attempted to wrap undefined property componentDidMount as function

Trying to test a component that fetches data in componentWillMount, calls setState() with the received data and renders a number of child components, like this:

export default class MyComponent extends React.Component {
    constructor() {
        super();
        this.state = {
            items: []
        };
    }

    componentWillMount() {
        Api.fetchItems().end((err, response) => {
            if (response && response.ok) {
                this.setState({ items: response.items });
            }
        });
    }

    render() {
        return (
            <div className="items-list">
                { this.state.items.map((item, i) =>
                    <Item item={ item } key={ i }/>
                ) }
            </div>
        );
    }
}

The test is very basic and almost 1to1 taken from the example:

import sinon from 'sinon';
import { expect } from 'chai';

it('mounts correctly', () => {
    sinon.spy(MyComponent.prototype, 'componentDidMount');
    const wrapper = mount(<MyComponent />);
    expect(MyComponent.prototype.componentDidMount.calledOnce).to.equal(true);
});

Unfortunately, the test fails:

TypeError: Attempted to wrap undefined property componentDidMount as function
      at Object.wrapMethod (node_modules/sinon/lib/sinon/util/core.js:106:29)
      at Object.spy (node_modules/sinon/lib/sinon/spy.js:41:26)
      at Context.<anonymous> (MyComponent.spec.js:21:19)

Any ideas? It seems the component doesn’t even mount at all, adding a expect(wrapper.hasClass('items-list')).toBe(true) fails as well.

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 15 (6 by maintainers)

Most upvoted comments

sinon can’t stub over non-existent properties. If you add a componentDidMount() {} to your component, then it should work.

Looks like your <App /> is wrapped by a redux’s connect HOC. The prototype you’re seeing is for that. You would need to get the wrapped instanced from Connect or just import your component (without the connect() wrapper) and mock the props that redux is providing.

I think componentDidMount is a typo and @doque was indeed referring to componentWillMount, because I am getting the same error: TypeError: Attempted to wrap undefined property componentWillMount as function.

 sinon.spy(App.prototype, 'componentWillMount');

I am mounting this way:

const rendered = mount(<App />, { context: { store: store }});

I am testing a component wrapped in a Redux Provider and if I log the App.prototype, componentWillMount is simply not there:

Connect {
  shouldComponentUpdate: [Function: shouldComponentUpdate],
  computeStateProps: [Function: computeStateProps],
  configureFinalMapState: [Function: configureFinalMapState],
  computeDispatchProps: [Function: computeDispatchProps],
  configureFinalMapDispatch: [Function: configureFinalMapDispatch],
  updateStatePropsIfNeeded: [Function: updateStatePropsIfNeeded],
  updateDispatchPropsIfNeeded: [Function: updateDispatchPropsIfNeeded],
  updateMergedPropsIfNeeded: [Function: updateMergedPropsIfNeeded],
  isSubscribed: [Function: isSubscribed],
  trySubscribe: [Function: trySubscribe],
  tryUnsubscribe: [Function: tryUnsubscribe],
  componentDidMount: [Function: componentDidMount],
  componentWillReceiveProps: [Function: componentWillReceiveProps],
  componentWillUnmount: [Function: componentWillUnmount],
  clearCache: [Function: clearCache],
  handleChange: [Function: handleChange],
  getWrappedInstance: [Function: getWrappedInstance],
  render: [Function: render],
  componentWillUpdate: [Function: componentWillUpdate] }

The property is not there, so sinon is not seeing it. Am I missing something obvious? Is this even an Enzyme issue?

Just for the record, spying on other methods works just as expected:

sinon.spy(Api, 'fetchItems');
mount(<MyComponent />);
expect(Api.fetchItems.calledOnce).to.be.true;