The recent project uses React.js, and React Bootstrap library. We found the following unittest setting works well:
- runner: karma
- framework: mocha
- JSX transformer: babel
- dependency management: webpack
Setup
SUT (subject under test) : e.g. ./static/js/components/myComponent.js
tests: e.g. /static/js/components/__tests__/myComponent-test.js
package.json
"devDependencies": {
"babel-core": "^5.3.0",
"babel-loader": "^5.3.0", # default install babel 6.+, but we need version 5.3+ to compile react-booststrap library
"expect": "^1.13.4",
"karma": "^0.13.16",
"karma-chrome-launcher": "^0.2.2",
"karma-cli": "^0.1.2",
"karma-mocha": "^0.2.1",
"karma-webpack": "^1.7.0",
"mocha": "^2.3.4",
"react": "^0.14.3",
"react-addons-test-utils": "*",
"react-bootstrap": "*",
"react-dom": "~0.14.0",
"webpack": "^1.12.9",
}
karma.conf.js
var webpack = require(‘webpack’);
module.exports = function (config) {
config.set({
browsers: [‘Chrome’],
singleRun: true,
frameworks: [‘mocha’],
files: [
‘tests.webpack.js’
],
preprocessors: {
‘tests.webpack.js’: [‘webpack’]
},
reporters: [‘dots’],
webpack: {
module: {
loaders: [
{test: /\.jsx?$/, exclude: /node_modules/, loader: ‘babel-loader’}
]
},
watch: true
},
webpackServer: {
noInfo: true
}
});
};
tests.webpack.js
var context = require.context(‘./static/js/components’, true, /-test\.jsx?$/);
context.keys().forEach(context);
Run test
npm test
Test strategies
Render the SUT
var myComponent = TestUtils.renderIntoDocument(<MyComponent prop1={prop1}/>);
Verify the SUT
expect(myComponent.state.state1).toBe(‘someState’);
expect(myComponent.props.prop1).toBe(‘someProp’);
Verify SUT’s DOM
// assume SUT is rendered as
contents
var node = ReactDOM.findDOMNode(myComponent);
expect(node.childNode.textContent).toBe(‘contents’);
Verify SUT’s child component
Several ways to access SUT’s child nodes:
- Using React component’s ref property
- Via the rendered DOM
- TestUtils methods, e.g. TestUtils.scryRenderedDOMComponentsWithClass
Need to distinguish between an React component instance and a DOM node instance.
var childComponent = myComponent.refs.childRef;
expect(childComponent).toExist();
expect(childComponent.state.state1).toBe(‘someState’);
expect(childComponent.props.prop1).toBe(‘someProp’);
Verify SUT’s multiple child components of the same class/tag
var childNodes = TestUtils.scryRenderedDOMComponentsWithClass(myComponent, ‘label’);
expect(childNodes.length).toBe(3);
Verify SUT’s child node
var childComponent = myComponent.ref.childRef;
var childNode = ReactDOM.findDOMNode(childComponent);
expect(childNode.textContent).toBe(‘something’);
Simulate SUT event
// assume SUT is rendered as
contents
var nodeHavingOnClickEvent = ReactDOM.findDOMNode(myComponent).firstChild;
TestUtils.Simulate.click(nodeHavingOnClickEvent); // invoke node <span>’s onClick event
Simulate changes to SUT’ properties or state
myComponent.state.state1 = ‘anotherState’;
TestUtils.Simulate.change(myComponent);
Simulate changes to ReactBootstrap.Input’s value
/** e.g. render() : function() {
return (
);
}
*//
var input = myComponent.refs.input;
input.getInputDOMNode().value = ‘newValue’;
TestUtils.Simulate.change(input.getInputDOMNode());
Test ReactBooststrap’s modal
/**
ReactBooststrap’s modal has many child components, e.g. title, body, etc.
e.g. MyModal
render: function() {
return (
{this.props.title}
{this.props.body}
);
}
*/
it(‘does not render when props.show is false’, ()=>{
// render in the document
var modal = TestUtils.renderIntoDocument(
<MyModal show={false} />
);
var notFound = ReactDOM.findDOMNode(modal.refs.title);
expect(notFound).toNotExist();
});
it(‘renders when props.show is true’, () => {
// render in the document
var modal = TestUtils.renderIntoDocument(
<MyModal show={true} />
);
var found = ReactDOM.findDOMNode(modal.refs.title);
expect(found).toExist();
});
References