React has become the framework of choice recently within the JavaScript community. And the tools for testing React components has also gained lot of attention.
Enzyme by Airbnb has been one of the popular choices for testing React components. In this article, we will look at how write tests with Enzyme and Mocha.
Installation
We need Node.js installed to begin with. Follow the link, download Node.js for installation instructions.
Now create a folder for your project and then run the following command in command line.
npm init
Babel
We need Babel to compile JSX code.
npm install babel-cli ––save
npm install babel-preset-react babel-preset-es2015 ––save
Webpack
We are going to use Webpack to build our project.
npm install webpack webpack-dev-server babel-loader ––save–dev
React
npm install react react-dom ––save
Mocha
We need to install testing tools:
npm install chai enzyme mocha jsdom react-addons-test-utils ––save–dev
Setting Up
Create .babelrc
In order to create a preset to use Babel, we have to create a .babelrc file.
{
"presets": ["airbnb", "es2015", "stage-0"]
}
We need to install the presets:
npm install babel-preset-stage-0 babel-preset-stage-airbnb ––save–dev
Create a webpack.config.js
var path = require('path');
var webpack = require('webpack');
// env
var buildDirectory = './dist/';
module.exports = {
entry: './lib/main.jsx',
devServer: {
hot: true,
inline: true,
port: 7700,
historyApiFallback: true,
},
resolve: {
extensions: ['', '.js', '.jsx'],
},
output: {
path: path.resolve(buildDirectory),
filename: 'app.js',
publicPath: 'http://localhost:7700/dist',
},
externals: {
'cheerio': 'window',
'react/lib/ExecutionEnvironment': true,
'react/lib/ReactContext': true,
},
module: {
loaders: [{
test: /\.jsx?$/,
exclude: /(node_modules|bower_components)/,
loader: 'babel',
query: {
presets: ['react', 'es2015', 'stage-0'],
},
}],
},
plugins: [],
};
The configuration for externals
will help enable enzyme to work properly.
Create browser.js
We create a setup file in order to provide a realistic browser environement using jsdom
.
require('babel-register')();
var jsdom = require('jsdom').jsdom;
var exposedProperties = ['window', 'navigator', 'document'];
global.document = jsdom('');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
if (typeof global[property] === 'undefined') {
exposedProperties.push(property);
global[property] = document.defaultView[property];
}
});
global.navigator = {
userAgent: 'node.js'
};
documentRef = document;
The package.json Scripts
Add the following to the scripts sections of the package.json
file.
"test": "mocha -w test/helpers/browser.js test/*.spec.js",
"dev:hot": "webpack-dev-server --hot --inline --progress --colors --watch --display-error-details --display-cached --content-base ./"
},
Creating Components
Create a containers
folder and a LoginForm.js
file.
import React from 'react';
import FormField from '../components/FormField';
class LoginForm extends React.Component {
render() {
return (
<form>
<FormField model="user.username" label="Username" type="text" />
<FormField model="user.password" label="Password" type="password"/>
<button>Log in</button>
</form>
);
}
}
export default LoginForm;
Create a components
folder and a FormField.js
file.
import React from 'react';
const FormField = ({ label, type }) => {
return (
<field>
<label>{label}</label>
<input type={type} />
</field>
);
};
export default FormField;
Testing Components.
Create a test
folder and create a LoginForm.spec.js
file in it.
import React from 'react';
import { mount, shallow } from 'enzyme';
import { expect } from 'chai';
import LoginForm from '../containers/LoginForm';
import FormField from '../components/FormField';
describe('<LoginForm/>', function () {
it('should have a button to submit the form', function () {
const wrapper = shallow(<LoginForm/>); // highlight
expect(wrapper.find('button')).to.have.length(1);
});
it('should have props for model, label and type', function () {
const wrapper = shallow(<LoginForm/>);
expect(wrapper.props().model).to.be.defined;
expect(wrapper.props().label).to.be.defined;
expect(wrapper.props().type).to.be.defined;
});
it('contains an <FormField/> component', function () {
const wrapper = mount(<FormField/>);
expect(wrapper.find(FormField)).to.have.length(1);
});
});
Not the highlighted line: The shallow
method from enzyme will allow us to “shallowly” render a component.
This type of rendering is used to isolate one component for testing and ensure child components do not affect assertions.
mount
The mount
method helps real rendering that will actually render your component into a browser environment. If you are creating full React components (and not just stateless components), you will want to use mount to do testing on the lifecycle methods of your component.
Now run the tests in the command line:
npm test
Conclusion
We have worked through creating a simple React component, installing test tools and running actual tests. I hope this tutorial will help you to kick start testing with React.