React makes compositing components easy. However testing them can get ugly if you are not careful. Consider this example:
var ChildA = React.createClass({
displayName: "ChildA",
render: function(){
return (<div>A in the house</div>);
}
});
var ChildB = React.createClass({
displayName: "ChildB",
render: function(){
return (<div>B in the house</div>);
}
});
var Parent = React.createClass({
displayName: "Parent",
render: function(){
return (<div>
<ChildA data={this.props.data.a}></ChildA>
<ChildB data={this.props.data.b}></ChildB>
</div>);
}
});
It is very tempting to write a unit test that renders Parent and asserts that the html has 2 divs. One that contains "A in the house" and another that contains "B in the house". There is a problem with this approach. What happens if ChildA's render method is altered to render "A is outta here"? Should the unit test for Parent break? No; it should not, but it will. To avoid this you need to test that Parent creates ChildA and ChildB with the correct data, instead of indirectly testing the behavior of ChildA and ChildB. Testing the behavior of ChildA and ChildB should be reserved for their respective unit tests. To do this we need a way to Mock ChildA and ChildB and intercept the properties passed to them. Ideally our test should look something like this:
QUnit.module("Test a parent component", {
beforeEach: function () {
this.sandbox = Sinon.sandbox.create();
//Set up the spec helper. Since React.createElement is static use sandbox.
//Pass in the components you want to mock
new ReactSpecHelper(this.sandbox).stubChildren([ChildA, ChildB]);
},
afterEach: function () {
this.sandbox.restore();
}
});
QUnit.test("It renders child A with the correct data", function(){
var data = {a: "a", b: "b"};
var view = TestUtils.renderIntoDocument(<Parent data={data} />);
var childA = TestUtils.scryRenderedDOMComponentsWithClass(view,"ChildA");
equal(childA.length, 1);
equal(childA[0].props.data, "a");
});
QUnit.test("It renders child B with the correct data", function(){
var data = {a: "a", b: "b"};
var view = TestUtils.renderIntoDocument(<Parent data={data} />);
var childA = TestUtils.scryRenderedDOMComponentsWithClass(view,"ChildB");
equal(childA.length, 1);
equal(childA[0].props.data, "b");
});
Attached is the utility I created to make this possible along with a sample test.
@pk-nb are you able to share a gist of your refactor? Thanks!