Testing with Jest
Joe Buza
What is Jest?
Jest is a JavaScript testing framework, used by Facebook to test all JavaScript code including React applications.
Why Jest?
-
Zero configuration
-
Fast
-
Snapshot testing
Zero Configuration
Jest virtualizes the javascript environment using jsdom and manages all the configurations inside the library.
Small Config File
// package.json
"dependencies": {
"react": "<current-version>",
"react-dom": "<current-version>"
},
"devDependencies": {
"babel-jest": "<current-version>",
"babel-preset-es2015": "<current-version>",
"babel-preset-react": "<current-version>",
"jest": "<current-version>",
"react-test-renderer": "<current-version>"
},
"scripts": {
"test": "jest"
}
// .babelrc
{
"presets": ["es2015", "react"]
}
// package.json
"devDependencies": {
"jest": "<current-version>"
},
"scripts": {
"test": "jest"
}
js
react
Fast
Jest runs tests in paraellel across worker processes
![](https://s3.amazonaws.com/media-p.slid.es/uploads/424912/images/3421820/jest-fast.gif)
Jest allows you to run tests that have changed. Uses git to detect the last file changed since the last commit.
jest -o
Jest Features
- watch mode
- code coverage
- helpful error messages
jest --watch
lets you automatically run the test suite on every file change.
"scripts": {
"watch:test": "jest --watch"
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/424912/images/3425724/1__npm_run_test__node_.png)
jest --coverage
{
"scripts": {
"test": "jest --coverage"
},
"jest": {
"testEnvironment": "node",
"collectCoverageFrom": [
"src/*.js"
],
"coverageThreshold": {
"global": {
"branches": 100,
"functions": 50,
"lines": 33,
"statements": 25
}
}
}
}
![](https://s3.amazonaws.com/media-p.slid.es/uploads/424912/images/3421844/jest-coverage.gif)
![](https://s3.amazonaws.com/media-p.slid.es/uploads/424912/images/3422433/jest-coverage-web.gif)
Helpful Error Messages
-
color coded
-
stack traces
![](https://s3.amazonaws.com/media-p.slid.es/uploads/424912/images/3425715/1__npm_run_test__node_.png)
Getting Started
My Configuration
{
"name": "testing-with-jest",
"version": "0.1.0",
"private": true,
"devDependencies": {
"enzyme": "^2.7.0",
"enzyme-to-json": "^1.4.5",
"react-addons-test-utils": "^15.4.2",
"react-dom": "^15.4.2",
"react-scripts": "0.8.5"
},
"dependencies": {
"react": "^15.4.2",
"react-dom": "^15.4.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"jest": {
"testEnvironment": "node",
"collectCoverageFrom": [
"src/*.js"
],
"coverageThreshold": {
"global": {
"branches": 100,
"functions": 50,
"lines": 33,
"statements": 25
}
}
}
}
Snapshot Testing
Jest can capture snapshots of React trees or other serializable values to simplify UI testing.
Before Snapshot Testing
Enzyme
JavaScript Testing utility for React that makes it easier to assert, manipulate, and traverse your React Components' output
Link Component
import React, {Component, PropTypes} from 'react'
const STATUS = {
NORMAL:'normal',
HOVERED:'hovered'
}
class Link extends Component{
static propTypes = {
page: PropTypes.string,
style: PropTypes.object
}
state = {
class: STATUS.NORMAL
}
_handleMouseEnter = () => {
this.setState({class: STATUS.HOVERED})
}
_handleMouseLeave = () => {
this.setState({class: STATUS.NORMAL})
}
render(){
return (
<a
style={{...style, ...this.props.style}}
className={this.state.class}
href={this.props.page || '#'}
onMouseEnter={this._handleMouseEnter}
onMouseLeave={this._handleMouseLeave}
>
{this.props.children}
</a>
)
}
}
const style = {
textShadow: '2px 2px darkblue'
}
export default Link
import React from 'react'
// Third Party
import { shallow } from 'enzyme'
// Local
import Link from '../Link'
describe('<Link/>', ()=>{
let wrapper
beforeEach(()=>{
wrapper = shallow(
<Link page=""/>
)
})
it('should render', ()=>{
expect(wrapper).toBeDefined()
})
it('should have class normal by default', ()=>{
expect(wrapper.props().className).toBe('normal')
})
it('should have class hovered when mouse enters', ()=>{
wrapper.simulate('mouseEnter')
expect(wrapper.props().className).toBe('hovered')
})
it('should have class normal when mouse leaves', ()=>{
wrapper.props().onMouseEnter()
wrapper.props().onMouseLeave()
expect(wrapper.props().className).toBe('normal')
})
it('should have href of # when no page is passed in', ()=>{
expect(wrapper.props().href).toBe('#')
})
it('should have correct link when passed in', ()=>{
const page = "https://www.facebook.com"
wrapper.setProps({page})
expect(wrapper.props().href).toBe(page)
})
it('should pass children', ()=>{
const children = 'MY LINK'
wrapper.setProps({children})
expect(wrapper.text()).toMatch(children)
})
})
Test using Enzyme
Result
![](https://s3.amazonaws.com/media-p.slid.es/uploads/424912/images/3424707/package_json_-_testing-with-jest.png)
Snapshot Test
// Link.react-test.js
import React from 'react';
import Link from '../Link.react';
import renderer from 'react-test-renderer';
it('Link changes the class when hovered', () => {
const component = renderer.create(
<Link page="http://www.facebook.com">Facebook</Link>
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
tree.props.onMouseEnter();
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
tree.props.onMouseLeave();
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
Snapshot
exports[`<Link/> snapshots Link changes the class when hovered 1`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
style={
Object {
"textShadow": "2px 2px darkblue",
}
}>
Facebook
</a>
`;
exports[`<Link/> snapshots Link changes the class when hovered 2`] = `
<a
className="hovered"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
style={
Object {
"textShadow": "2px 2px darkblue",
}
}>
Facebook
</a>
`;
exports[`<Link/> snapshots Link changes the class when hovered 3`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
style={
Object {
"textShadow": "2px 2px darkblue",
}
}>
Facebook
</a>
`;
Pros
-
Writing tests is easy
-
Easier inspection
-
Saves time
-
Provides a clear view of what changed after each update whether major or minor
Cons
-
Requires manual verification
-
Easy to accept snapshots that have problems.
-
Can create fragile tests.
Use with care
![](https://s3.amazonaws.com/media-p.slid.es/uploads/424912/images/3422525/Christoph_Pojer_on_Twitter____VilleImmonen__Vjeux_but_I_need_to_stress_that_when_using_snapshots_it_s_integral_that_the_resulting_file_is_being_reviewed_properly__.png)
A robust code review process is required.
![](https://s3.amazonaws.com/media-p.slid.es/uploads/424912/images/3422543/Dan_Abramov_on_Twitter____ArtyomTrityak_In_any_case__Jest_works_with_Enzyme_without_a_problem__.png)
Future is bright
![](https://s3.amazonaws.com/media-p.slid.es/uploads/424912/images/3422553/C2Zm5OjUsAANcG-.gif)
upcoming feature
Resources
-
http://benmccormick.org/2016/12/10/saving-time-with-jest/
-
http://benmccormick.org/2016/09/19/testing-with-jest-snapshots-first-impressions/
-
http://randycoulman.com/blog/2016/09/06/snapshot-testing-use-with-care/
-
https://blog.grommet.io/post/2016/09/01/how-we-landed-on-jest-snapshot-testing-for-javascript
-
https://hackernoon.com/testing-react-components-with-jest-and-enzyme-41d592c174f#.9iau5xi4n
Questions?
Testing with Jest
By Joe Buza
Testing with Jest
Jest is a JavaScript testing framework, used by Facebook to test all JavaScript code including React applications.
- 3,135