React components allow unit tests in JS to be much easier.
These exampes require sinonjs
, jsdom
, enzyme
, mocha
, chai
These are examples of the three APIs that you get with Enzyme. Remember, that a majority of the test cases will need shallow
.
Testing lifecycle events, use mount
.
If you need to test a component that gets rendered or returns just HTML, you can use render
.
File structure
- test
- repository_test.spec.js
- github_widget.spec.js
- user_image.spec.js
- scripts
- mocha_runner.js
- package.json
- src
- components
- repositories.js
- components
// package.json
...
Note: the --debug flag allows us to use the debugger statements and interact with the debugger through the command line
Note: There is also a `node-intercept` that allows you to tap into Chrome Dev tools
"scripts": {
...
"test": "mocha --debug --require scripts/mocha_runner.js ./test/**/*.spec.js",
...
}
...
// reposity_test.js
import React from 'react-native';
import { shallow } from 'enzyme';
import { expect } from 'chai';
// import a component
import Repositories from '../src/components/repositories';
describe('<Repositories />', () => {
it('should render one number of repos specified by top prop', () => {
const wrapper = shallow(<Repositories repositories={repos} top={2} />);
expect(wrapper.find(Repository)).to.have.length(2);
});
it('should display repos ordered by stargazers' () => {
const sortedTestData = repos.sort((a, b) => b.stargazers_count - a.stargazers_count);
const wrapper = shallow(<Repositories repositories={repos} top={2} />);
// find each child <Repository /> with <Repositories />
const topRepos = wrapper.find(Repository);
// for each <Repository /> found, test out correct values
topRepos.forEach((repo, index) => {
expect(repo.prop('url')).to.equal(sortedTestData[index.url]);
expect(repo.prop('name')).to.equal(sortedTestData[index].name);
expect(repo.prop('language')).to.equal(sortedTestData[index].language);
expect(repo.prop('stars')).to.equal(sortedTestData[index].stars);
});
});
});
This requires use of js-dom
// mocha_runner.js
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;
require('babel-core/register');
// github_widget.spec.js
import React from 'react-native';
import { mount } from 'enzyme';
import { expect } from 'chai';
import sinon from 'sinon';
// js dom has also been used as an example for these headless browser testing
// import a component
import GithubWidget from '../src/components/GithubWidget';
import UserDetails from ...
// import all the other components
// found in the expect below
...
describe('<GithubWidget />', () => {
it('should render all sub-components', () => {
const wrapper = mount(<GithubWidget username="test" />);
expect(wrapper.containsAllMatchingElements([
<UserDetails />,
<UserStats />,
<hr />,
<Repositories />,
<Footer />
])).to.equal(true);
});
it('should display repos ordered by stargazers' () => {
const sortedTestData = repos.sort((a, b) => b.stargazers_count - a.stargazers_count);
const wrapper = shallow(<Repositories repositories={repos} top={2} />);
// find each child <Repository /> with <Repositories />
const topRepos = wrapper.find(Repository);
// for each <Repository /> found, test out correct values
topRepos.forEach((repo, index) => {
expect(repo.prop('url')).to.equal(sortedTestData[index.url]);
expect(repo.prop('name')).to.equal(sortedTestData[index].name);
expect(repo.prop('language')).to.equal(sortedTestData[index].language);
expect(repo.prop('stars')).to.equal(sortedTestData[index].stars);
});
});
it('should call componentDidMount once' () => {
// create a spy
sinon.spy(GithubWidget.prototype, 'componentDidMount');
mount(<GithubWidget username ="test />");
expect(GithubWidget.prototype.componentDidMount.calledOnce).to.equal(true);
});
});
// user_image.spec.js
import React from 'react-native';
import { render } from 'enzyme';
import { expect } from 'chai';
// Component
import UserImage from './UserImage';
describe('<UserImage />', () => {
it('should have a <div /> element with .gh-widget-photo class', () => {
const wrapper = render(<UserImage />);
expect(wrapper.find('div').attr('class')).to.equal('gh-widget-photo');
});
});