React Unit Test Using Mocha
Heres the dependencies you should install for a solid test set up:
npm i babel-core babel-preset-es2015 babel-preset-react babel-preset-stage-2
npm i chai mocha enzyme sinon
npm i react react-addons-test-utils react-dom
n.b. react-addons-test-utils and react-dom are implicit dependencies of react.
Then you’ll need to pass babel-core/register to mocha, so in your test script:
"test": "mocha -w --compilers js:babel-core/register '[glob to match test files]'"
// a glob might be something like: **/*.spec.js
// this would match any file whose names ends with .spec.js in any directory
You’ll need to declare your babel plugins, which you can do in your package.json or a .babelrc.
// .babelrc
{
"presets": ["es2015", "react", "stage-2"],
"ignore": "node_modules"
}
// package.json
"babel": {
{
"presets": ["es2015", "react", "stage-2"],
"ignore": "node_modules"
}
}
some components to test Lets add two simple components. One is a save button and the other rendered a list of names passed through props as an array. By no means a perfect component, the myComponent component will demonstrate how we can test state, rendering and click events/even handlers.
saveButton.js
import React from 'react';
export default (props) => {
return ();
}
myComponent.js
import React from 'react';
import Save from './saveButton';
export default React.createClass({
getInitialState() {
return {
title: 'list'
}
},
selected(evt) {
this.setState({
title: `you selected ${evt.target.innerText}`
})
},
renderListItem(name) {
return (
);
},
renderList() {
if (this.props.names) {
return (
);
}
return null;
},
render() {
return (
);
}
});
myComponent.spec.js Heres out test file
import React from 'react';
import { shallow, mount } from 'enzyme';
import { expect } from 'chai';
import sinon from 'sinon';
import MyComponent from './myComponent';
import SaveButton from './saveButton';
describe('', () => {
describe('when rendered', () => {
let component, sandbox, eventHandler;
beforeEach(() => {
sandbox = sinon.sandbox.create();
eventHandler = sandbox.stub();
component = shallow();
});
it('should have a as it\'s root elment', () => {
expect(component.is('div')).to.equal(true);
});
it('should have a save button', () => {
expect(component.containsMatchingElement(SomeButton)).to.equal(true);
});
describe('when the save button is clicked', () => {
it('should call any callback functionality', () => {
sinon.assert.notCalled(eventHandler);
component.find(SomeButton).simulate('click');
sinon.assert.calledOnce(eventHandler);
});
});
});
describe('when given a list of names', () => {
let component, items, title;
let names = ['Andrew', 'Bob', 'Steve', 'Colin'];
beforeEach(() => {
component = shallow();
items = component.find('li');
title = component.find('h3');
});
it('should show a unordered list containing those names', () => {
expect(items).to.have.length(names.length);
items.forEach((item, index) => {
expect(item.text()).to.equal(names[index]);
});
});
describe('the lists title', () => {
it('should initially say \'list\'', () => {
expect(title.text()).to.equal('list');
});
describe('when a list item has been clicked', () => {
let item;
beforeEach(() => {
item = component.find('li').first();
item.simulate('click', {target: {innerText: item.text()}});
title = component.find('h3');
});
it('should update the title with the item clicked', () => {
expect(title.text()).to.equal('you selected Andrew');
});
});
});
});
});
To test our code we run the test script:
npm test
If everything is working correctly you’ll see 6 passing tests and a warning about adding keys to iterated output (i.e. when we map over a function that returns a component we should also add a unique key).