Using Mocha with JSPM

js-logo

A disturbing amount of open (and closed) source javascript projects that I’ve worked on are severely lacking in tests, whether unit or otherwise. Of course, I do find time to click around the apps and make sure they basically work, and while I find that works for smaller apps, it’s obvious that if you want long-term stability, and any one to even take a look at your app, one of the most basic requirements is having properly functioning tests.

Also, to put it frankly, a lack of tests in 2016 (Happy new years, by the way), is just a mark of inexperience. To embark on a software-building experience these days without taking some time to write unit/integration/acceptance tests is like hunting Moby Dick without bringing along any life vests. Yes, you might spear the beast a few times, but you’re going to feel not-so-good when she inevitably breaks your ship in half and you didn’t bring along anything to make swimming easier.

I fully realize that while I write this, Firegraph stands without nary a test to it’s name. I’ll get to it, I promise, I’m just an inexperienced programmer :).

To make this not the case for my newest project, I created an actual Github issue (tied to the first release milestone, of course) to add proper tests (at least unit tests) to the project. As I embarked on this today, I really began to realize how much I’d forgotten about testing. At one point in my career, I was (or at least felt) acutely aware of how Jasmine, Mocha, PhantomJS/NightmareJS, all fit together, and which parts of the browser/nodejs were involved in using these frameworks. This time, however, I’m working with a JSPM project, and was actually almost completely unsure how everything really fit together.

For those who might not be quite up to speed:

  • Mocha – A javascript test runner targetting NodeJS. However, since browser javascript is indeed javascript, it did indeed fit my usecase, despite the browser-context that the app is going to be mostly used in
  • JSPM – A javascript dependency manager that includes many features (bundling, extensive configuration, ES6) by default. JSPM is wonderful, and perhaps more importantly, has a wonderfully straightforward site, with great demos to get you started quickly. JSPM is powered in large part by SystemJS, which is a module loader like RequireJS or Webpack.
  • PhantomJS – An engine for running and maintaining the state of a browser, and sending instructions (like “click this button” or “type this text in the input field with id x”). One of the remarkable features of PhantomJS is that it does it’s work without opening an actual browser that you can see (this is generally called “headless” testing), this is often valued for the speed (as the browser doesn’t have to spend time actually rendering things).
  • NightmareJS – An collection of functions that make writing tests that use PhantomJS easier. This library gets more useful the more functionally you use it, as functional composition gives you great mileage when reusing nightmare-code. Nightmare offers conveniences on top of the PhantomJS engine.

To successfully use two technologies like Mocha and JSPM together often requires knowing some of the quirks and real from-scratch use models of both technologies, and the borders that have to be merged/mended to allow them to work harmoniously. This is where frameworks that do everything for you, or even just scaffold things for you tend to let you down, sometimes you need to be able to recreate such an atmosphere from scratch. I had just a tad bit of trouble even getting mocha installed for this project as well as realizing it was what I wanted to use over something like Jasmine (which is more for a browser-context).

After re-acclimating myself to Mocha (and remembering how much I liked their “nyan” reporter when I first saw it), I embarked on the google knowledge quest to merge these two things together. Here’s the step-by-step I was trying to complete

Getting started with Mocha and JSPM

  1. Make a test/ directory
  2. Make some test/test.js file that will contain the actual testing code
  3. Transpile?
  4. Be able to import dependencies from the actual app, which is stored in a completely separate folder
  5. How do you test react components?

Setting up Transpilation

While the first two issues were easy, the third one threw me for a little loop. Since JSPM handles transpilation with Babel (the ES6/ES2015 transpiler) manually, it was a bit of a thinking exercise to figure out whether I needed to transpile everything manually. The first thought that popped into my head was that since Mocha was NodeJS based, all I needed to do was add the --harmony flag to some node invocation somewhere, and everything would be fine. Of course, mocha did in fact support the --harmony flag, but it wasn’t quite what I expected. The *actual* solution was to install a special babel compiler for mocha, babel-register, and use that when I ran mocha. So roughly the solution ended up looking like:

$ npm install --save-dev babel-register
$ mocha --compilers js:babel-register

Of course, I added that to the test script in package.json (while using the locally installed version of mocha in ./node_modules/.bin).

Setting up dependency imports for the tests

Setting up the SystemJS dependencies was the next difficulty to overcome. It’s hard to test a file that includes ES6/ES2015 imports that can’t be resolved, due to the glue that SystemJS provides not being set up properly. Luckily, SystemJS provides a relatively easy-to-use javascript API. This meant that I could include something like this in my code to start getting things to compile somewhat properly:

import "should";
import _ from "lodash";

import System from "systemjs"; // Importing SystemJS code
import "../config"; // Importing the same SystemJS config the app is using

... Actual test code ...

This got me a little further, but I ran into a problem when I started using SystemJS as follows:

describe("Some thing", () => {
  it("Should expose the right stuff", done => {
    SystemJS.import("./package")
      .then(pkg => {
        // Do some stuff
      })
      .catch(done);
  });
});

I was finding that the imports weren’t being resolved properly, but that was because the config that was loading was somehow deriving file paths that started from the base of the file system. This means paths like file:///jspm_packages/package/lib/package were being checked, and of course, this translated to checking the root file system for a folder called “jspm_packages”, when of course all you find there is things like “usr” and “home” and “var”.

The fix for this happened to be relatively painless (though it introduced quite some worry and anxiety initially): Changing the SystemJS config to use a relative path for the baseURL config property:

... Somewhere in config.js ...
baseURL: "./", // Changed from "/"
... the rest of config.js ...

After that fix, I could run tests properly. Here’s what the code ended up looking like:

mport System from "systemjs";
import "../config";
import "should";
import _ from "lodash";

const EXPECTED_CONSTANTS = [
  "SOME",
  "LIST",
  "OF",
  "CONSTANTS"
]

describe("Project constants", () => {

  // Ensure that the generated constants export contains the functions we expect                                                                                                                                                                                                  
  it("Should contain all the constants we expect", (done) => {
    System.import("./js/constants") // note this is relative to top level project folder, not test file
      .then(constants => {
        constants.default.should.have.keys(EXPECTED_CONSTANTS); // note here that I had to access the default export
        done();
      })
      .catch(done);
  });

});

With this test saved in the tests/ folder, I ran mocha with the following command:

./node_modules/.bin/mocha --compilers js:babel-register -R nyan

And all of a sudden I had a functioning test!

I’m sure this will get more difficult once I start trying to add acceptance tests (and using Phantom/Nightmare), but for now, it’s nice to know the project has one (possibly useless and pointless) test.