Tests

There are a few testing frameworks out there for JS — mocha, jasmine, jest (which is React specific).

Each of them has their pros and cons, and at Docker we've decided to use a combination of Mocha (for tests) and Chai (for assertions). We also use JSDom to create a virtual DOM environment which we use to render React components.

Why?

  • Mocha and Chai are easy to use, fast, and compatible with ES7 via Babel
  • Mocha doesn't automatically mock everything in the way Jest does. That's a ton of magic you normally end up undoing. Basically, don't use Jest.
  • JSDom is lighter weight than spinning up a browser or PhantomJS instance
    • This means faster tests
    • This means faster continuous integration cycles
  • JSDom means we can virtually render without shallow rendering.
    • This is super important because shallow rendering means you can't test decorators. Shallow rendering only renders one level deep. Decorators typically wrap your custom component with a parent — so only the parent decorator will render.

The setup

Setting up Mocha, Chai and JSDom to work with a Redux project is relatively easy. There are three steps:

  1. Install everything via NPM
  2. Change the package.json test command to use Mocha
  3. Add a JSDom setup
1. Installing packages

We need a few packages to get our tests running:

npm install --save-dev mocha chai jsdom
2. Changing the test command of package.json

Our package.json commands should look like this;

  "scripts": {
    "test": "$(npm bin)/mocha  --compilers js:babel/register --recursive --require ./test.setup.js src/scripts/**/_tests/"
    ...
  }

Whoa. Complex AF, right? Here's what it does:

  • Sets up Mocha to use BabelJS' "register" hook. Essentially every included file is put through Babel's preprocessor, which means you can write your tests in ES7 and use ES7 includes.
  • Requires a test setup file which fakes a DOM using JSDom. This file is included below.
  • Uses a splat to search for every folder which contains a _tests directory. All JS files in these folders will be run as tests.
3. The JSDom setup

The test.setup.js file contains this:

import { jsdom } from 'jsdom';

global.document = jsdom('<!doctype html><html><body></body></html>');
global.window = document.defaultView;
global.navigator = global.window.navigator;

Kinda simple - set up a few global variables which use JSDom to create a fake document. Boom, you're good to render in pure JS with React.

Writing tests

If you're not so familiar with writing tests first read the Mocha documentation. Writing tests for a Redux app isn't that different from what you'd find in lodash.

Some pointers for things you should test:

  • Test your middleware. This is basically everything that DRYs up your app.
  • Test your reducers to ensure they're modifying state as expected.
  • Test your selectors to ensure they're working as expected. You don't want to have to render a component to validate selectors.
  • Test to ensure that your components render correctly given variants of data (for example, if something is hidden/shown depending on props). This is for bonus marks if you want an A+ from your teacher.
  • Test your utility files and functions that are shared.

Bonus: optimizing CI tests with Docker

When you run tests via CI you'll need to npm install every time you run tests. This can be hella slow. Instead, we can build a docker image which uses a hash of package.json to cache build images. We only end up waiting for a docker pull instead of an npm install, which is much faster.

TODO: Explain how to create a docker image that caches NPM installs then uses this image for tests