Setting Up Mocha

React Native CLI installs the Jest testing framework by default, but in the last few versions of React Native it's had some stability issues.

Instead, we'll use the Mocha test framework. Its syntax is very similar to Jest. It takes a little work to set up, but it's worth it!

Removing Jest

First, let's remove Jest since we won't be using it. If you're using Expo Jest is not installed by default. If you're using React Native CLI, run the following:

$ yarn remove jest babel-jest

Then, remove the following config and script from package.json:

   "scripts": {
-    "start": "node node_modules/react-native/local-cli/cli.js start",
-    "test": "jest"
+    "start": "node node_modules/react-native/local-cli/cli.js start"
   },
...
     "metro-react-native-babel-preset": "0.51.1",
     "react-test-renderer": "16.6.3"
-  },
-  "jest": {
-    "preset": "react-native"
   }

Installing Mocha

Mocha's ecosystem is split into several separate packages. We'll install the following, which are typically used together:

  • Mocha, the test runner
  • Chai, the assertion library
  • Sinon, the test double library
  • sinon-chai, which allows for running more readable expectations against Sinon test doubles

Install all of them:

$ yarn add --dev mocha \
                 chai \
                 sinon \
                 sinon-chai

Next, add an NPM script to run mocha:

   "scripts": {
-    "start": "node node_modules/react-native/local-cli/cli.js start"
+    "start": "node node_modules/react-native/local-cli/cli.js start",
+    "test": "mocha \"test/**/*.spec.js\""
   },

Create a test folder at the root of your project, then add a mocha.opts file to configure mocha. Add the following to it:

--require @babel/register
--require chai/register-expect
--require test/setup

These flags do the following:

  • Enables Babel transpilation so you can use modern JS features
  • Sets up the expect() function so you can use it in any file without importing it
  • Loads a custom setup.js file you'll create for additional setup

Let's create that test/setup.js file now and add the following:

import chai from 'chai';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';

global.sinon = sinon;
chai.use(sinonChai);

This does the following:

  • Makes sinon available globally so you don't need to import it
  • Loads sinon-chai so you can do more readable assertions against Sinon test doubles

With this, our setup should be done.

Smoke Test

To confirm Mocha is working, create a test/unit folder, then create a test/unit/smoke.spec.js file. Add the following contents:

describe("truth", () => {
  it("is true", () => {
    expect(true).to.equal(true);
  });
});

Run the tests with yarn test. You should see output like the following:

$ yarn test
yarn run v1.13.0
$ mocha "test/**/*.spec.js"


  truth
    ✓ is true


  1 passing (29ms)

Configuring ESLint

Mocha has a number of globally-available functions, and we've set up Chai and Sinon to use globals as well, so ESLint will complain about these. We need to configure ESLint to accept them.

If you aren't already using ESLint in your project, it's easy to install in a React Native project. Add the following packages:

yarn add -D eslint \
            babel-eslint \
            eslint-config-codingitwrong \
            eslint-plugin-import \
            eslint-plugin-jsx-a11y \
            eslint-plugin-react

Then create an .eslintrc.js file at the root of your project and add the following:

module.exports = {
  extends: [
    'plugin:react/recommended',
    'codingitwrong',
  ],
  settings: {
    react: {
      version: '16.5',
    },
  },
  parser: 'babel-eslint',
  env: {
    'browser': true,
    'es6': true,
    'node': true,
  },
  rules: {
    'react/prop-types': 'off',
  }
};

Most code editors can be configured to run ESLint rules as you edit. You can also add an NPM script to do so:

   "scripts": {
     "start": "node node_modules/react-native/local-cli/cli.js start",
+    "lint": "eslint \"**/*.js\"",
     "test": "jest"
   },

To configure ESLint to allow Mocha's globals, add them to the list of globals ESLint will accept:

     'es6': true,
     'node': true,
   },
+  globals: {
+    'after': true,
+    'afterEach': true,
+    'before': true,
+    'beforeEach': true,
+    'describe': true,
+    'expect': true,
+    'it': true,
+    'sinon': true
+  },
   rules: {
     'react/prop-types': 'off',
   }