Test React componentWillReceiveProps
Testing React Components has been easier and more enjoyable than any previous UI unit testing I've done in the past. Components that have interesting things happen in lifecycle methods have a little more setup to get tested. Components that use the componentWillReceiveProps
method are in this category.
React Test Setup
Not all lifecycle methods require as much setup in a test as componentWillReceiveProps
. This is because:
- This method is concerned with changing props.
- Changing props directly on a React Component (even under test) is against the React code of conduct
So, we need something that is legal to change... state
! We need to not modify our subject under test and simply pass it new props
.
My solution is to create a React Component specifically for the test. This Component will be a parent to the subject under test, on which we can set state
. We'll design it so that this state is transferred to the child Component under test.
React Component using componentWillReceiveProps
You might have a React Component to test that looks like this:
var ComponentToTest = React.createClass({
getInitialProps() {
return {
myProp: "blank"
};
},
getInitialState() {
return {
modified: "still blank"
};
},
componentWillReceiveProps(nextProps) {
this.setState({
modified: nextProps.myProp + "IsSoModified"
});
},
render() {
return <div class="displayed">{this.state.modified}</div>
}
});
There is nothing particularly interesting about this subject beyond the fact that it uses componentWillReceiveProps
. When new props are received, internal state is modified. In real life, more interesting things like data fetching or complex calculations might be done here and then stored in state. We simply are matching the scenario of needing to verify something when componentWillReceiveProps
is called.
A Parent Test Component
The test to exercise componentWillReceiveProps
on the above Component might look like this:
var React = require("react/addons");
var TestUtils = React.addons.TestUtils;
it("displays a modified state upon changing props", function () {
var TestParent = React.createFactory(React.createClass({
getInitialState() {
return { testState: "init" };
},
render() {
return <ComponentToTest ref="sot" myProp={this.state.testState} />
}
}));
var parent = TestUtils.renderIntoDocument(TestParent());
parent.refs.sot.props.myProp.should.eql("init");
parent.setState({
testState: "somethingElse"
});
parent.refs.sot.props.myProp.should.eql("somethingElse");
parent.refs.sot.state.modified.should.eql("somethingElseIsSoModified"); // assert #1
var child = TestUtils.scryRenderedDOMComponentsWithClass(parent, "displayed")[0];
child.getDOMNode().innerText.should.eql("somethingElseIsSoModified"); // assert #2
});
The TestParent
component is created specifically for this test. It renders the Component under test. It sets a ref
attribute to it for easy access. Once we render the parent, the initial state was sent as the prop to the child Component. As soon as we setState
on the parent, a new prop is sent to the child, triggering componentWillReceiveProps
.
Finally, I've included two assertion styles. Again, there are going to be more interesting things that you're asserting here in real life. In this case I'm verifying that state that renders directly to the UI is set in our lifecycle method. I can interrogate the state directly. Here, we reach into the child Component state for assertion method #1. That may sound bad, but remember that TestParent
was created only in the context of this test anyway, so the level we're reaching through to grab child state is just test code. Assertion method #2 is to go to the DOM to verify final output from the state change.
There is definitely more setup here to make this happen. I feel like usually testing a React Component doesn't require this much test code.
componentWillReceiveProps
in Action
Check out this jsbin of a component that uses componentWillReceiveProps
to make a simple display change.
Check out this slightly modified jsbin which does the test assertion.
What methods have you used to test Components that use either componentWillReceiveProps
or other interesting lifecycle methods?