Developing on Staxmanade

Browser only MochaJS tests using SystemJS

(Comments)

I've been poking at SystemJS (you may have heard of it through JSPM) and one of the first things I like to setup when playing with a new JS framework is a way to run MochaJS unit tests which allow me to test-drive my prototypes and in this case the best part is we don't have to do any local command line installations or crazy gulp/grunt builds. We can right in the browser start writing plain ES6 and prototype using a library from npm or github.

SystemJS is a very exciting project with it's ability to import modules/code right from your JS code itself. You can write your JS using ES6 import syntax and in many cases SystemJS will magically import code via NPM or GitHub directly.

TL;DR

If you want to skip the details below and just see the plnkr.co go right ahead!

How to run our first Mocha test

First we need to get a simple web page setup. I'm going to use plnkr.co as it allows me to specify multiple files. This will allow me to more easily componentize my code for cleaner extraction into a project, gist or other...

Once you have a basic Plunker setup go ahead and delete everything except index.html of for now.

Now we're ready to start throwing our code in here... But before you do open your browser's developer tools. I'm using Chrome on the Mac so Cmd+Option+j will do it. We need to be able to see the javascript console in case we see any errors with SystemJS loading of modules.

index.html <- paste the below in for you're Plunker index.html.

<!DOCTYPE html>
<html>

<head>
  <script src="https://jspm.io/[email protected]"></script>
  <script type="text/javascript">
    System.import('./testInit.js');
  </script>
</head>

<body>
</body>

</html>

With the above in the index.html you should see some errors printed to the console as SystemJS is trying to load ./testInit.js (but we haven't created it yet).

Before we create the testInit.js file let's first create a couple sample MochaJS test files that we want to test.

Here's our first test file: name it mochaTest1.js

Something cool about this test is once we get mocha wired up correctly, this test shows how seamlessly you can take a dependency on a 3rd party library like chaijs for help with assertions.

import { expect } from 'chai';

describe("This is a describe", function() {
  it("sample test that should pass", function() {
    expect(true).to.equal(true);
  });
  it("sample test that should fail", function() {
    expect(true).to.equal(false);
  });
});

Create another test file mochaTest2.js

import { expect } from 'chai';

describe("This is another describe", function() {
  it("sample test that should pass", function() {
    expect(true).to.equal(true);
  });
  it("sample test that should fail", function() {
    expect(true).to.equal(false);
  });
});

Creating two test files allows this sample to show how you can easily create and test multiple modules.

The meat and potatoes

Now is the juicy part on how to get Mocha to play nicely with this setup and run our tests.

Create a file and call it testInit.js (same as we named in our index.html and referenced it via System.import('./testInit.js')) and paste the below.

Feel free to read through it as I commented it thoroughly.

//
// This tells SystemJS to load the mocha library
// and allows us to interact with the library below.
//
import mocha from 'mocha';

//
// This defines the list of test files we want to load and run tests against.
//
var mochaTestScripts = [
  './mochaTest1.js',
  './mochaTest2.js'
];

//
// If you have a global or two that get exposed from your
// tests that is expected you can include them here
//
var allowedMochaGlobals = [
  'jQuery'
]


//
// Mocha needs a <div id="mocha"></div> for the browser
// test reporter to inject test results in to the U.I.
// Below just injects it at the bottom of the page. (You can get fancy here)
// Maybe you create a button in your website and allow anyone to run tests.
// Check out https://staxmanade.com/2015/03/in-app-unit-tests/ for more on the thought
//
var mochaDiv = document.createElement('div');
mochaDiv.id = "mocha";
document.body.appendChild(mochaDiv);

//
// Importing mocha with JSPM and ES6 doesn't expose the usual mocha globals.
// I found this is one way to manually expose the globals, however if you know of a better way please let me know...
//
mocha.suite.on('pre-require', function(context) {
  var exports = window;

  exports.afterEach = context.afterEach || context.teardown;
  exports.after = context.after || context.suiteTeardown;
  exports.beforeEach = context.beforeEach || context.setup;
  exports.before = context.before || context.suiteSetup;
  exports.describe = context.describe || context.suite;
  exports.it = context.it || context.test;
  exports.setup = context.setup || context.beforeEach;
  exports.suiteSetup = context.suiteSetup || context.before;
  exports.suiteTeardown = context.suiteTeardown || context.after;
  exports.suite = context.suite || context.describe;
  exports.teardown = context.teardown || context.afterEach;
  exports.test = context.test || context.it;
  exports.run = context.run;

  // now use SystemJS to load all test files
  Promise
    .all(mochaTestScripts.map(function(testScript) {
      return System.import(testScript);
    })).then(function() {
      mocha.checkLeaks();
      mocha.globals(allowedMochaGlobals);
      mocha.run();
    }, function(err) {
      console.error("Error loading test modules");
      console.error(err);
    });

});

mocha.setup('bdd');

Please let me know if you know of an easier way to get access to the mochas globals using SystemJS. The below works, but is a bit uncomfortable.

MochaJS Tests Right in the Browser...

How awesome is this. Couple bits of bootstrap code, and we can go author whatever we want right in the browser using ES6 (err EcmaScript 2015) and we're off and running.

warning NOT FOR PRODUCTION WORKFLOWS (yet)! warning

This approach is primarily for allowing quick prototyping. Don't implement a complete app like this and then expect any performance. SystemJS can potentially download a large number of dependencies and you should read up on JSPM production workflows.

Happy Browser-Only Testing.