Enzyme: Getting props trough shallow rendering is not working with defaultProps

Created on 12 May 2016  路  14Comments  路  Source: enzymejs/enzyme

Issue:
When using shallow rendering and setting default props inside React component with getDefaultProps() (React.createClass) or with static defaultProps (ES7), calling wrapper.props() or wrapper.prop('myProp') does not return any values (undefined) (it should return default ones)

Although default props ARE set inside component, i cannot test them from outside with enzyme.
If i'll use mount , its working correctly.

Gist:
https://gist.github.com/nehaleem/7fc6d6b2940bc523a2009a566ebfed97

invalid

Most helpful comment

try wrapper.instance().props

All 14 comments

I was able to reproduce the error here: Aweary/enzyme-test-repo/blob/issue-#384/test.js.

+1 it looks like shallow returns the immediate component returned by the render method while mount returns the actual component. In the original gist, shallow returns the <button>...</button> and mount returns the <AlertButton/>. Is this expected behavior?

+1 it looks like shallow returns the immediate component returned by the render method while mount returns the actual component. In the original gist, shallow returns the and mount returns the . Is this expected behavior?

This is expected behavior, shallow returns the root node not the actual composite component. Closing, since this is actually expected behavior for Enzyme.

mount is not returning default props either

You shouldn't need to assert on defaultProps; you should only assert on the props you pass in, and on the behavior of the component. defaultProps are an implementation detail.

Even tho they are just an implementation detail, lack of them may break the component. I'm currently having a problem where the props that should have a default value are undefined and tests do not pass as the component being mounted relies on those default property values to work.

@zgredzik yes, but you should thus be testing the behavior that would break - not the presence or absence of default props.

@ljharb I might have been not explicit enough: I am testing other behaviours but the component crashes while being mounted, thus making the test fail.

The component itself requires some props to exist in a specified format: either by using isRequired in propTypes and forcing the component's user to pass the data, or defaultProps to ensure the optional data that has not been passed in is still in the format required.
If I was not able to specify defaultProps, I'd have to either always pass a dummy (default) value explicitly or assume any optional property will default to undefined and add additional checks in the code to make sure that it's not.

When I want to test the behaviour of the component that does not depend on the optional properties I don't see a reason to be forced to pass them.

Unless I got the whole concept of how mount is actually supposed to work wrong.

The component gets the defaulted prop values; it's just .props() that isn't. I'm not seeing the issue.

I guess I misunderstood what @mgenev was reporting and prematurely assumed that was exactly the same problem I encountered. Furthermore I've dug deeper into my own issue and it seems my lack of the default value is because of a problem in a 3rd party library I'm using and not a problem with mount. My bad.

try wrapper.instance().props

Yes, For a shallow wrapper, you can call a wrapper.instance().props.propName to get the value of respective prop.

I have the problem using context when a child accesses its parent's props:

class Parent {
  static defaultProps {
    hello: 'world'
  };

  static childContextTypes = {
    parent: PropTypes.object.isRequired
  };
  getChildContext() {
    return {
      parent: this
    };
  }
}

class Child {
  static contextTypes = {
    parent: PropTypes.object.isRequired
  };

  render() {
    console.log(this.context.parent.props.hello); // hello is undefined
  }
}

const parent = new Parent({});
shallow(
  <Child />,
  {context: {parent: parent}}
);

Hackish solution:

class ParentEnzymeFix extends Parent {
  constructor(props) {
    const defaultProps = {
      hello: 'world'
    };
    super({...defaultProps, ...props});
  }
}

const parent = new ParentEnzymeFix({});

Edit:

This generates a React warning:
Warning: ParentEnzymeFix(...): When calling super() in 'ParentEnzymeFix', make sure to pass up the same props that your component's constructor was passed.
Solution:

function new_Parent(props) {
  const defaultProps = {
    hello: 'world'
  };
  return new Parent({...defaultProps, ...props});
}

const parent = new_Parent({});
Was this page helpful?
0 / 5 - 0 ratings