CTA BG
Blog
React Testing

React Testing

React Testing

Lets face it, testing is important but testing javascript UIs is not often straightforward.

Now for React. React is a excellent choice for your front-end view logic. I love React for how productive it makes me feel. However, one downside of the React ecosystem is that there are so many choices to make in terms of what tools you will use. Varying choices include Webpack, Browserify, ES6 and Babel to manage your state, business logic and request data. It requires a lot of sifting through. To make it easy, here is a set of good choices for testing React that have worked well for us.

Tools


Mocha & Chia

Mocha is a test runner that has been around for a good while. It provides describe() and it() functions for BDD style testing. It is reminiscent of Rspec in the Ruby world. Chia is often used with Mocha as it brings a whole host of assertions for whichever style of testing you like.


Chia

With Chia you have the options of using expect, should, and assert style assertions making it flexible for anyone's taste.


Enzyme

Enzyme is a React component testing library from airbnb. It provides multiple, optimized ways to render React components and make assertions against them. Enzyme uses JSdom for some of its functionality and will be included as a dependency.


Optional Tools


Babel Register

If you are using ES6, this is a must as it compiles your tests and app code on the fly, enabling you to write your tests in ES6.


Sinon

If you need mocking I suggest looking at Sinon. With that said, I have not needed much mocking in my testing of React components and I suggest seeing how far you can go without having to add mocking.


Redux Testing (bonus)

There is an added bonus if you are using Redux, hence you will want to grab redux-mock-store. It enables you to set up a store with exactly the state that you want, making the testing of asynchronous actions easier.

 

Install Tools


Required tools:

npm install -g mocha

npm install --save-dev mocha chai enzyme jsdom jsdom-global react-addons-test-utils

Optional tools:

Babel Register and presets:

npm install --save-dev babel-core babel-preset-airbnb babel-preset-es2015 babel-preset-react babel-preset-stage-0

Sinon:

npm install --save-dev sinon

Redux Mock Store:

npm install --save-dev redux-mock-store

Setup


Here is what to do once you have installed everything.

Mocha.opts


Mocha.opts is a config file where you can define default options that will always be run when you use the Mocha command:

--compiler: defines a compiler to use before files are required
--require: used to require test suite dependancies
--recursive: runs all tests in ./test and tests in sub-directories in ./test
--ui: changes the functions that you use for Mocha



--compiler js:babel-register
--require ignore-styles
--require jsdom-global/register
--require ./test/test_helper.js
--recursive
--ui bdd

./test/mocha.opts

Test Helper


It is helpful to create a test-helper.js where you handle testing dependancies and any code that needs to run before the test suite fires up.
import { expect } from 'chai';
import sinon from 'sinon';

global.expect = expect;
global.sinon = sinon;

./test/test_helper.js

Package.json


Register your test command in Package.json

...,
"scripts": {
"test": "mocha",
"test:watch": "npm test -- --watch"
}
package.json

Let's Test


You should now have a working testing setup for React, but lets take our shiny, new setup for a test drive. This is going to be an involved example with some of the optional tools.


Component


import React, { Component } from 'react'
import Auth from './AuthService'

export default class LoginForm extends Component {
constructor(props) {
super(props)
this.state = {
email: '',
password: ''
}
}

componentWillMount() {
if (Auth.user !== undefined) {
Auth.goToDashBoard()
}
}

onSubmitForm() {
Auth.Login(this.state)
}

render() {
return (
<div>
<div>
<label>Email</label>
<input
type="text"
value={this.state.email}
ref={(ref) => this.emailField = ref}
onChange={() => this.setState({email: this.emailField.value})}/>
</div>
<div>
<label>Password</label>
<input
type="password"
value={this.state.password}
ref={(ref) => this.passwordField = ref}
onChange={() => this.setState({password: this.passwordField.value})}/>
</div>
<button
type="button"
onClick={this.onSubmitForm}>
Login
</button>
</div>
)
}
}

Tests


We need to import react so jsx works in this file


import React from 'react'
import { shallow } from 'Enzyme'
import LoginForm from '../src/LoginForm'


describe('LoginForm', () => {
it('renders 2 inputs and a button', () => {
var wrapper = shallow(<LoginForm />)
expect(wrapper.find('input')).to.have.length(2)
expect(wrapper.find('button')).to.have.length(1)
})

it('test lifecycle method, redirects if user exists', () => {
var wrapper = shallow(<LoginForm />)
expect(wrapper.instance().componentWillMount.calledOnce).to.be
expect('check that redirect happened').to.be
})

it('test instance method', () => {
var wrapper = shallow(<LoginForm />)
var login = wrapper.instance().onSubmitForm()
expect('check that login happened').to.be
})

Steven McFarlane
Steven McFarlane