angular unit testing without karma

February 2017 ยท 2 minute read

I grew tired of the startup time of Karma. I also grew tired of PhantomJS. I wanted something faster, something better.

If Nirvana was a thing, and I got to go there I’d be doing tape all day ‘erry day.

But, to ease up on the transition I decided to go with Mocha + Chai + Sinon.

Hell, I was already using all of those packages to begin with (albeit hidden behind less than ideal Karma framework plugins) so, the migration shouldn’t require a lot.

$ rm karma.conf.js
$ yarn remove karma karma-sinon-* karma-chai* karma-* # die, die, die!

Cool. Clean. Let’s get started with the setup for testing Angular without a browser. speed!

$ yarn add mocha mocha-clean chai jsdom glob sinon-chai chai-as-promised --dev

Goodie. We have packages. Time to setup the test harness.

// harness.js
require('mocha-clean'); // cleaner stack traces, yes please

const glob = require('glob');
const jsdom = require('jsdom');
const chai = require('chai');
const sinon = require('sinon');

const sinonChai = require('sinon-chai');
const asPromised = require('chai-as-promised');

chai.use(asPromised);
chai.use(sinonChai);

global.document = jsdom.jsdom();
global.window = document.defaultView;

global.navigator = window.navigator || {};
global.Node = window.Node;
global.addEventListener = window.addEventListener;
global.MouseEvent = window.MouseEvent;
global.KeyboardEvent = window.KeyboardEvent;
global.Event = window.Event;
global.btoa = window.btoa;
global.FormData = window.FormData;
global.FileReader = window.FileReader;
global.File = window.File;

window.beforeEach = global.beforeEach;
window.afterEach = global.afterEach;
window.before = global.before;
window.after = global.after;
window.mocha = true;

// require angular _after_ we have our window object set up & ready
require('angular/angular');
require('angular-mocks')

global.angular = window.angular;
global.sinon = sinon;
global.expect = chai.expect;

// require your main application entry point
// to get all of them modules going and dependencies hooked up
require('./src/main.js');

// require all the test files
glob.sync('./src/app/**/*.spec.js').forEach(require);
$ mocha harness.js

๐ŸŽ‰


Rerunning in dev?

A lil’ shell script to run chokidar-cli + mocha + “run-only-tests-for-the-file-that-changed”.

# if you pass --all to the script,
# no regexing will happen and all tests will be rerun on change to any file

#!/bin/bash
export NODE_ENV=test && \
export all=$1 && \
    chokidar --initial --silent 'src/**/*.js' \
    -c 'if [[ {event} = "change" && "$all" != "--all" ]]; then rx=$(basename {path} | cut -d. -f1); else rx=""; fi;
        mocha -g "$rx" harness.js'

Need babel?

Add the following to your mocha call:

--require babel-core/register

๐ŸŽ‰


Before/After?

Karma + PhantomJS @ 943 tests: 44s
Mocha + JSDom @ 1913 tests: 27s