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
// 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'); }); });