Effective Mocha Testing

Mocha Testing Tips & Tricks for JS Developers

Tips and Tricks on mocha testing. I’ve seen a couple of applications using Mocha where import and require is used in every single testfile. This is especially intensive when doing testing in React where you include multiple packages to perform your testing. Thus I’m writing this post with some small tips & tricks on how to reduce the boilerplate needed.

We are going from something like this (worst case):

1
2
3
4
5
6
7
8
9
10
11
12
13
import chai from 'chai';
import React from 'react';
import ReactDOM from 'react-dom';
const expect = chai.expect;
const assert = chai.assert;

// Absolute worst case, typically this is performed in a setup-file
// haven't seen anything this bad yet.
import Adapter from 'enzyme-adapter-react-16';
import { configure, shallow, mount, render } from 'enzyme';
configure({ adpater: new Adapter() });

describe('Tests goes here...', () => {....});

to this:

1
2
// :)
describe('Tests goes here...', () => {....});

tl;dr

If you just want the good stuff, import this setup.js file using mocha --require ./path/to/setup.js and you have a pretty basic setup for testing React using Mocha and Chai. (continue reading if you want some tips on how to handle Webpack aliases in your tests)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// -----------------
// JSDom
// -----------------
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
const { document } = new JSDOM('<!doctype html><html><body></body></html>').window;
global.document = document;
global.window = document.defaultView;
global.requestAnimationFrame = callback => {
    setTimeout(callback, 0);
};

global.window.matchMedia =
    window.matchMedia ||
    (() => {
        return { matches: false, addListener: () => {}, removeListener: () => {} };
    });

global.navigator = {
    userAgent: 'node.js',
    platform: 'Win32'
};

// ------------------
// Chai
// ------------------
const chai = require('chai');
global.expect = chai.expect;

// ------------------
// Enzyme
// ------------------
const { shallow, mount, render, configure } = require('enzyme');
const Adapter = require('enzyme-adapter-react-16');
configure({ adapter: new Adapter() });
global.shallow = shallow;
global.mount = mount;
global.render = render;

// ------------------
// React (if using ProvidePlugin from webpack all of these are needed)
// ------------------
const React = require('react');
const PropTypes = require('prop-types');
const ReactDOM = require('react-dom');
global.React = React;
global.PropTypes = PropTypes;
global.ReactDOM = ReactDOM;


// ------------------
// Helpers
// ------------------
global.itRenders = Component => {
    const wrapper = shallow(Component);
    expect(shallow(Component).length).to.eql(1);
    return wrapper;
};

/*
  Disable webpack-specific 'loaders' for tests
  extensions: [".styl",".css",".png",".jpg",".gif",".svg",".ico"]
*/

require.extensions['.scss'] = function() {
    return null;
};

Mocha –require

Using mocha you can require files to be included before you run your tests. In this file you can also perform your setup. If you using babel you –require babel-register mocha --require babel-core/register. I still recommend using mocha --opts though (see below).

1
2
// Way too long
mocha --require ./test/helpers/dom.js --require ./test/helpers/globals.js --require ./test/helpers/ignores.js ./client/**/*.spec.js

Mocha –opts

To use an options file for mocha tests you can just do mocha --opts ./path/to/mocha.opts (see above for reasons why) If you don’t supply mocha with the --opts mocha will automatically try to look for the options file in ./test/mocha.opts

mocha.opts

My mocha.opts usually looks something like this (for testing React)

1
2
3
4
--require babel-register
--require ./client/test/helpers/dom.js
--require ./client/test/helpers/ignores.js
--require ./client/test/helpers/globals.js

The files that are imported is doing the following

  • dom.js – to JSDom for testing React without a browser.
  • ignores.js – disable some file-types that would otherwise require webpack.
  • globals.js – general setup with globals that I want to use in most tests.

dom.js

Setup of JSDom

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// -----------------
// JSDom
// -----------------
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
const { document } = new JSDOM('<!doctype html><html><body></body></html>').window;
global.document = document;
global.window = document.defaultView;
global.requestAnimationFrame = callback => {
    setTimeout(callback, 0);
};

global.window.matchMedia =
    window.matchMedia ||
    (() => {
        return { matches: false, addListener: () => {}, removeListener: () => {} };
    });

global.navigator = {
    userAgent: 'node.js',
    platform: 'Win32'
};

ignores.js

Disable some file formats (since we’re not using webpack)

1
2
3
4
5
6
7
/*
  Disable webpack-specific 'loaders' for tests
  extensions: [".styl",".css",".png",".jpg",".gif",".svg",".ico"]
*/

require.extensions['.scss'] = function() {
    return null;
};

setup.js

This is where the magic happens, set some global parameters and helpers so that we don’t have to import them everywhere.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// ------------------
// Chai
// ------------------
const chai = require('chai');
global.expect = chai.expect;

// ------------------
// Enzyme
// ------------------
const { shallow, mount, render, configure } = require('enzyme');
const Adapter = require('enzyme-adapter-react-16');
configure({ adapter: new Adapter() });
global.shallow = shallow;
global.mount = mount;
global.render = render;

// ------------------
// React (if using ProvidePlugin from webpack all of these are needed)
// ------------------
const React = require('react');
const PropTypes = require('prop-types');
const ReactDOM = require('react-dom');
global.React = React;
global.PropTypes = PropTypes;
global.ReactDOM = ReactDOM;


// ------------------
// Helpers
// ------------------
global.itRenders = Component => {
    const wrapper = shallow(Component);
    expect(shallow(Component).length).to.eql(1);
    return wrapper;
};

Example

So when the setup is done I don’t have to import anything. And I have a helper called itRenders() to verify that a component actually renders.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import App from './index.js';

describe('<App />', () => {

    // Not really necessary since we test itRenders() on every other test.
    // used as an example.
    it('Should successfully render.', () => {
        itRenders(<App />);
    });

    it('Should successfully render with class.', () => {
        expect(itRenders(<App className="cls" />).find('.cls').length).to.eql(1);
    });
   
});

Caveats

But what about eslint?

You’ll get warnings since eslint won’t know about your globals when testing. So I make sure to add those globals to my .eslintrc file.

1
2
3
4
5
6
7
8
    "globals": {
        "React": true,
        "ReactDOM": true,
        "PropTypes": true,
        "shallow": true,
        "expect": true,
        "itRenders": true
    }

Webpack Aliases?

If you are like me and love using Webpack aliases for easier imports you’ll need to ‘transfer’ them to be compiled using babel somehow. And there’s a great package to help you with that babel-plugin-webpack-alias. Since I only want to use them when running tests I add them to .babelrc using the env key. (GitHub / NPM)

1
2
3
4
5
6
7
8
9
10
11
12
{
    "presets": ["react", "babel-preset-env"],
    "plugins": ["transform-object-rest-spread"],
    "env": {
        "test": {
            "plugins": [["babel-plugin-webpack-alias", { "config": "./webpack.config.js" }]]
        },
        "development": {
            "plugins": ["react-hot-loader/babel"]
        }
    }
}

That’s it

Now you’ve got my take on how I like to make the testing experience smoother when I’m using mocha. One last tip, use wallaby.js for your IDE of choice to support in writing your mocha tests. You can read about that VSCode Extension in my other blog post. What’s your best tips on how to make your testing more effective?

Please follow and like SnappyJS:

Leave a Reply

Your email address will not be published.