Testing a React-Redux app using Jest and Enzyme

Gethyl George Kurian
Jan 5, 2017 · 11 min read

This is a very simple test which doesn’t cover async or mocking

I wanted to start with a very basic app and this is why I have not done anything async in the app. And that is also why I didn’t use the code which I had used from my earlier post like Isomorphice React-Redux (You should check this posts if you haven’t already and this as well setting up a GraphQL Server :) )

Why did I choose Jest as the testing framework?

Jest was never considered and I was thinking of using either Mocha or Jasmine, as these were the two frameworks I was comfortable with as I have used them on projects related to AngularJS earlier.

  • Ability to run tests in parallel. This is where Mocha loses the battle for me. Well for this example running the tests in parallel is trivial. But for a huge app with lot of tests, it will be quick if the tests could run in parallel.
  • Snapshot testing. This is a really cool feature I like. This helps me in reducing the number of tests I have to write as I just create a snapshot and if anything changes in my component, I will get an error when the snapshot is generated the next time.
  • Jest uses Jasmine for assertion and since I have used Jasmine earlier, it was easy to write tests for me.
  • Code coverage is available right out of the box.
  • In-built Manual mocking (Haven’t tried yet)

Setting up

The easiest way start working with the example I have provided is to

git clone https://github.com/Gethyl/ReactReduxTestingUsingJestEnzyme
cd ./ReactReduxTestingUsingJestEnzyme

Understanding the dependencies and devDependencies installed

Take a minute to look at the package.json to see the libraries you just installed from the above step.

"dependencies": {
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"babel-preset-stage-0": "^6.16.0",
"react": "^15.4.1",
"react-dom": "^15.4.1",
"react-redux": "^5.0.1",
"redux": "^3.6.0"
},
"devDependencies": {
"babel-jest": "^18.0.0",
"babel-loader": "^6.2.10",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"enzyme": "^2.7.0",
"jest": "^18.1.0",
"react-addons-test-utils": "^15.4.1",
"react-test-renderer": "^15.4.1",
"redux-mock-store": "^1.2.1",
"webpack": "^1.14.0",
"webpack-dev-server": "^1.16.2"
}

Running Tests

If you check the script in package.json I have added ”test”: “jest”

yarn test
  1. calculatorActions.spec.js — This has unit tests for Redux Action Creators.
  2. calculatorReducers.spec.js — This has unit tests for Redux Reducers.

Understanding our tests and test-suites

Let us go through the below sections to understand the tests better.

1. Components/Connected Components(Home.spec.js)

Now in our case, I would like to test Home.js component. If you see the component, you will notice that it’s actually a smart component/Container or Connected Component as we will refer here.

import React from "react"
import ReactDOM from "react-dom"
import {connect} from 'react-redux'
import {addInputs, subtractInputs} from '../actions/calculatorActions'
const mapStateToProps = (state) => ({
output:state.output
})
export class Home extends React.Component{
render(){
let IntegerA,IntegerB,IntegerC,IntegerD;
return(
<div className="container">
......
....
..
</div>
);
}
}
export default connect(mapStateToProps)(Home)

Don’t use decorator @connect to test.

You should not use decorators to connect your component as shown below:-

@connect(mapStateToProps)
export default class Home extends React.Component{
import {Home} from ‘../src/js/components/Home’
beforeEach(()=>{       
wrapper = shallow(<Home output={output}/>)
})
<div>Output :
<input type="text" placeholder="Output" readOnly ref="output" value={this.props.output}></input>
</div>
import ConnectedHome,{Home} from '../src/js/components/Home'
import configureStore from 'redux-mock-store'
//*******************************************************************************************************
describe('>>>H O M E --- REACT-REDUX (actual Store + reducers) more of Integration Testing',()=>{
const initialState = {output:10}
let store,wrapper
beforeEach(()=>{
store = createStore(calculatorReducers)
wrapper = mount( <Provider store={store}><ConnectedHome /></Provider> )
})
it('+++ check Prop matches with initialState', () => {
store.dispatch(addInputs(500))
expect(wrapper.find(Home).prop('output')).toBe(500)
});
});
import renderer from 'react-test-renderer'
// Snapshot for Home React Component
describe('>>>H O M E --- Snapshot',()=>{
it('+++capturing Snapshot of Home', () => {
const renderedValue = renderer.create(<Home output={10}/>).toJSON()
expect(renderedValue).toMatchSnapshot();
});
});
exports[`>>>H O M E --- Snapshot +++capturing Snapshot of Home 1`] = `
<div
className="container">
<h2>
using React and Redux
</h2>
<div>
Input 1:
<input
placeholder="Input 1"
type="text" />
</div>
<div>
Input 2 :
<input
placeholder="Input 2"
type="text" />
</div>
<div>
Output :
<input
placeholder="Output"
readOnly={true}
type="text"
value={10} />
</div>
<div>
<button
id="add"
onClick={[Function]}>
Add
</button>
<button
id="subtract"
onClick={[Function]}>
Subtract
</button>
</div>
<hr />
</div>
`;
yarn test -- -u    OR  npm test -- -u

2. ActionCreators(calculatorActions.spec.js)

We just try to assert that the actionCreators return the action that we expect.

import {addInputs,subtractInputs} from '../src/js/actions/calculatorActions'describe('>>>A C T I O N --- Test calculatorActions',()=>{
it('+++ actionCreator addInputs', () => {
const add = addInputs(50)
expect(add).toEqual({type:"ADD_INPUTS",output:50})
});
it('+++ actionCreator subtractInputs', () => {
const subtract = subtractInputs(-50)
expect(subtract).toEqual({type:"SUBTRACT_INPUTS",output:-50})
});
});

3. Reducers(calculatorReducers.spec.js)

Just like actionCreator, we assert reducer.

import calculatorReducers from '../src/js/reducers/calculatorReducers'describe('>>>R E D U C E R --- Test calculatorReducers',()=>{
it('+++ reducer for ADD_INPUT', () => {
let state = {output:100}
state = calculatorReducers(state,{type:"ADD_INPUTS",output:500})
expect(state).toEqual({output:500})
});
it('+++ reducer for SUBTRACT_INPUT', () => {
let state = {output:100}
state = calculatorReducers(state,{type:"SUBTRACT_INPUTS",output:50})
expect(state).toEqual({output:50})
});
});

Code Coverage

Code coverage comes right out of the box and all you have to do is issue the command as shown

yarn test -- --coverage  OR npm test -- --coverage

Conclusion

As mentioned earlier, I would like to hear from you how you prefer to test connected components, and what all you test. It will be a learning for me :)

Enjoy and Happy Coding, well Testing!!!

Netscape

A community dedicated to those who use JavaScript every day.

Gethyl George Kurian

Written by

Web developer | React | Nodejs | GraphQL | Angular 1.X

Netscape

Netscape

A community dedicated to those who use JavaScript every day.