In this article you will learn how to setup your environment and get started with React Unit Testing. The setup I am detailing here is inspired by this much longer yet amazing tutorial.
To get the most out of this post, it’s best if you have had some exposure to at least some of these frameworks/tools:
- Node.js and NPM: install packages and run custom scripts
- webpack: Basic configuration to compile code and launch a web server
- React: Basics of writing components
- mocha: Run unit tests with mocha
- chai: Basics of writing unit tests with chai
- Babel: Compile ES6 and React code with Babel
- ES6: Import statement and some of the new additions to the language
Project Setup
Everything we need to write our first React component.
NPM
Let’s get started with a new NPM project
$ npm init -y |
Dist Folder
Create a dist
folder and an dist/index.html
file that will be the shell for our React code.
Here is the HTML code we will use:
|
We will use webpack
to generate bundle.js
from the React code will write later on.
Webpack & Babel
Install Webpack and Webpack’s local web server module.
$ npm install --save-dev webpack webpack-dev-server |
If you have not done so, install these modules globally as well:
$ npm install -g webpack webpack-dev-server |
webpack
: base webpack modulewebpack-dev-server
: easy web server with webpack (defaults to http://localhost:8080/)
We will also need all the Babel compilers:
$ npm install --save-dev babel-core babel-loader babel-preset-react babel-preset-es2015 |
babel-core
: needed to do anything with babel. Install this globally if you have not already done so.babel-loader
: babel plugin for webpack.babel-preset-react
: babel plugin to compile our React JSX files.babel-preset-es2015
: babel plugin to compile our ES6/ES2015 code.
We are now ready to write the first version of webpack’s configuration: webpack.config.js
var path = require('path'); |
module
andresolve
contain the Babel configuration.output
contains the outputbundle.js
configuration.devserver
indicates which folder should be used as the root forwebpack-dev-server
.
React libraries
The start of the show:
$ npm install --save react react-dom |
react
: React base modulereact-dom
: to render React component to the DOM
Package.json - Babel configuration
In your package.json, add the following section to tell Babel to use the ES6 and React plugins. This is usually done in a .babelrc
file but I find it cleaner to put it in package.json
"babel": { |
App code
Let’s start building !
App requirements
This is what our app does. Starting with an array of strings, our component will generate one button for each of these strings. A click on any of the generated buttons should display the button text in a label.
src/index.jsx
Let’s start with a src/index.jsx
file that will hold our component:
import React from 'react'; |
We have a Component
React class with an options
property that will hold our string array.
src/Component.jsx
Here is the code for that component, put it in src/Component.jsx
:
import React from 'react'; |
- The initial text in our
div.display-text
label is"Click on a button"
. - We are generating buttons for each string in
this.props.options
. - A click on one of these buttons will update the state and the label’s text.
Pretty straightforward. At this stage, you can to run the app with:
$ webpack-dev-server |
and open your browser at http://localhost:8080/.
Unit testing
Let’s now look at why we are here in the first place. Unit testing our newly created Component
.
Unit testing libraries
Let’s start by installing and configuring our Unit testing environment. We will be using:
mocha
to run our tests andchai
to write them.jsdom
to emulate a web browser so that we won’t need a real browser to run our tests.react-addons-test-utils
which is the official React test helper library.
$ npm install --save-dev mocha chai jsdom react-addons-test-utils |
Configure jsdom
jsdom requires some configuration that we will put in a test/test-helper.js
file. Later, mocha
will run this file before running any test.
We need to set up a basic HTML environment for jsdom
and transfers the window
object and all its properties to Node’s global object so that mocha can access them seamlessly.
import jsdom from 'jsdom'; |
package.json - Test script
Our Mocha command is pretty complex so let’s add a script in the the package.json.
"scripts": { |
Now we can simply type npm run test
to run our tests.
Details on that command:
'test/**/*.@(js|jsx)'
: It runs all mocha unit tests in.js
and.jsx
files in the/test/
folder.--compilers js:babel-core/register
: It compiles the ES6 and React code with Babel. You might have to installbabel-core
globally to execute that command. Do so if you run into problems.--require ./test/test-helper.js
: It executes our jsdom configuration intest/test-helper.js
before running the tests.
Writing the first test
And the moment we have all been waiting for: our first test in test/Component-spec.jsx
.
Let’s start bu testing the button generation:
import React from 'react'; |
Imports
We are going to use some of the react test utils methods:
renderIntoDocument
: renders a component into a detached DOM.scryRenderedDOMComponentsWithTag
: finds all instances of components based on tag name
Test Flow
We start by rendering our Component
, then retrieve all the options
property.
Run the tests
Run this test using
$ npm run test |
Writing a second test
Before we go, let’s write a second test that check that the test has changed when a button is clicked:
import React from 'react'; |
Imports
Check the imports: we have added two more react test utils methods:
findRenderedDOMComponentWithClass
: which finds one unique instance of an element based on its css class.Simulate
: simulate user interaction, hereSimulate.click
.
Test Flow
We start by rendering our Component
, then retrieve all the <buttons>
and the label with the display text.
For each button we simulate a click and check that the text content of the label has changed and matched the button’s text.
Run the tests
Run both our tests using:
$ npm run test |
Hope you found this tutorial useful, I sure had a great time writing it :)