Setting up Unit Tests for React with Mocha, JSDOM and Enzyme

Before start coding your awesome React app, it is always a good idea to set up unit test infrastructure. Once test is set up, you can write tests whenever you feel necessary before waiting to complete the entire app development.

Mocha is a JavaScript test framework that runs on Node.js. JSDOM can mock the browser in the node execution environment. Enzyme is a React test utility, which simulate React component behaviours. With Enzyme, you can easily mount React component and write unit tests for the component’s expected behaviours.

This post is focused on JavaScript React. If you are interested in setting up unit tests with these tools, you can check out this post. The set up is slightly different, but quite similar between JavaScript and TypeScript. It is always good to know how to do it in both languages.

I prefer Mocha to Jest. I even prefer using Jasmine to Jest. This is mainly because Jest runs slow. Or more precisely, I haven’t really spent enough time to make Jest run fast. There must be a trick to run it faster by enabling caching or something. Check out this AirBnb blog about migrating from mocha to jest here. Jest is slow is probably not true. Jest also has nice features to write unit test for React. In fact, I wrote a small blog about it. Check it out here if you are more of a Jest person.

Setting up

(1) Installation

First of all, we need babel register used as a reporter in Mocha command. This is because tests run in Node.js execution environment and it does not support all the newer syntax used in React like import statement. You probably have @babel/core already installed for your React project. If so, you only need to install @babel/register.

If you are confused about babel, I wrote a blog about it. Check it out here.

npm i --save-dev @babel/core @babel/register @babel/plugin-transform-runtime

We can install all the testing tools. Enzyme needs adapter for the correct React version you are using.

npm i --save-dev mocha chai fake jsdom sinon
npm i --save-dev enzyme enzyme-adapter-react-16

Then, add @babel/transform-runtime plugin. This will enable Node.js execution environment to use React code. For example, export and import statements does not work without it.

{ 
  "presets": [
    "@babel/preset-react",
    "@babel/preset-env",
   
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties",
    "@babel/transform-runtime"
  ]
}

(2) Mocha command in Package.json

You need to add @babel/register and the jsdom set up file path (here, I called it browser.js). npm test now can run the test.

"test": "mocha -r @babel/register -r ./browser.js  **/test/**/*.spec.js --recursive"

(3) Adding jsdom config

Create a browser.js file in the root folder as below. All the code is from the documentation here. What is missing is enzyme configuration. You need to add it at the bottom of the file. It will set enzyme accessible from any test file.

That’s it. npm test will execute tests when you add them. Now let’s write some tests.

Unit Test Examples

Simple UI test example

Spy onSubmit handler example

Front-End
TypeScript: type aliases to check type equality

This post is to analyse how the type equality check by using type aliases proposed by Matt Pocock in his twitter post. These type aliases allow us to elegantly express type equality checks in unit tests. All we need to do is to pass the output and expected types in …

Front-End
Fixing it.only type error in Jest

If you are getting a type error with it.only in Jest, it could be due to incorrect TypeScript typings or incompatible versions of Jest and TypeScript. To resolve this issue, you can try the following steps: Make sure you have the latest versions of Jest and its TypeScript typings installed …

Front-End
yup conditional validation example

Here’s an example of a Yup validation logic where the first input field is optional but, if filled, it must contain only alphabetic characters, and the second input field is required: import * as Yup from “yup”; const validationSchema = Yup.object().shape({ firstField: Yup.string().matches(/^[A-Za-z]*$/, { message: “First field must contain only …