Photo by Artem Sapegin on Unsplash

Introduction

Prior to this effort, there were a few tests driven by QUnit. I originally had fixtures (HTML files full of DOM markup for tests to manipulate) for four different versions of jQuery and was doing some mocking of network communications. However, when I dove back into the project to work on it after more than a year without touching it, I was having trouble running the tests and even understanding what was going on.

So instead of investing more in QUnit, I decided to just take a look around and see if there were better, more modern, options available.

That's when I discovered Jasmine. And no, I can't tell you why I chose Jasmine over Mocha. I didn't do much comparison or shopping and this isn't mean to be a versus type post. I might just write all the same tests using Mocha to get a feel for both. But for now, I just want to tell you how I got up and running with Jasmine.

The Testing Stack

The first thing I learned was that I needed a test runner. Karma is what I went with as that seems to be the most popular.

Since this is a jQuery library (for now) and it is built around Ajax requests, I needed a few different plugins right from the start.

The jasmine-ajax plugin allows us to fake Ajax responses which are critical for testing the handlers. The jasmine-fixture plugin makes easy work of building up DOM elements as needed to manipulate and test against. This feature was a big win over having to maintain static HTML documents loaded with elements that my tests relied upon under QUnit.

As for the Karma test runner, I needed the karma-jquery adapter so that I could inject jQuery into the tests. And since we are testing in Jasmine, I needed the karma-jasmine adapter as well. Lastly, since we will be running these tests in PhantomJS we also would need karma-phantomsjs-launcher.

We end up with the following in our devDependencies:

"devDependencies": {
  "jasmine-ajax": "3.3.1",
  "jasmine-core": "2.8.0",
  "jasmine-fixture": "2.0.0",
  "karma": "1.7.1",
  "karma-cli": "1.0.1",
  "karma-coverage": "1.1.1",
  "karma-jasmine": "1.1.0",
  "karma-jquery": "0.2.2",
  "karma-phantomjs-launcher": "1.0.4",
  "phantomjs-prebuilt": "2.1.15",
}

You will notice in that list that we also have karma-coverage. This gives us code coverage reporting which is pretty cool. It allows us to know what parts of our code haven't been executed as part of our tests. While we won't know if a bit of code is properly tested without knowing our tests, we can be certain something isn't tested if it doesn't execute.

Configuration

For testing we can either run the karma command line directly or can run npm test as I set up the following package scripts:

"scripts": {
  "lint": "eslint src/eldarion-ajax-core.js src/eldarion-ajax-handlers.js tests/specs.js",
  "jasmine": "karma start tests/config.js --single-run",
  "release": "npm run uglify && npm run add-banner",
  "add-banner": "node banner.js",
  "uglify": "cat src/eldarion-ajax-core.js src/eldarion-ajax-handlers.js | uglifyjs --compress --mangle --comments \"/Copyright/\" -o dist/eldarion-ajax.min.js",
  "test": "npm run lint && npm run jasmine"
}

Karma is driven off a configuration that is stored in tests/config.js. What I'm using, so far, for eldarion-ajax is:

// Karma configuration
// Generated on Mon Oct 02 2017 17:31:27 GMT-0400 (EDT)
/* globals module */

module.exports = function(config) {
  'use strict';

  config.set({
    plugins: [
      'karma-jquery',
      'karma-jasmine',
      'karma-phantomjs-launcher',
      'karma-coverage'
    ],
    basePath: '',
    frameworks: ['jasmine', 'jquery-3.2.1'],
    files: [
      '../src/*.js',
      '../node_modules/jasmine-ajax/lib/mock-ajax.js',
      '../node_modules/jasmine-fixture/dist/jasmine-fixture.min.js',
      'specs.js'
    ],
    exclude: [
    ],
    preprocessors: {
      '../src/*.js': ['coverage']
    },
    reporters: ['progress', 'coverage'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['PhantomJS'],
    singleRun: false,
    concurrency: Infinity,
    coverageReporter: {
      dir: 'coverage',
      reporters: [
        {
          type: 'json',
          subdir: '.',
          file: 'coverage.json'
        },
        {
          type: 'html',
          subdir: 'report-html'
        }
      ]
    }
  });
};

You can see here where we've referenced those plugins that we previously installed and included the jasmine and jquery-3.2.1 frameworks. The files node includes what files and in what order to include in the running of the tests. The preprocessors is where we tell Karma to run coverage over all our source files while reporters is how we tell Karma the output we want to see.

The coverageReporter node is where we set up the types of output we want from coverage (which is using Istanbul under the covers). We want both a nice HTML version for local browsing as well as a json, machine-readable, format for usage by CodeCov.

Watch Mode

One really cool thing about the karma runner is you can run it in watch mode and it will rerun tests when it detects changes to your source files or your tests. As you can see in this gif after I start the running and all 45 tests pass, I make a breaking change to my source (off-screen), and it immediately reruns and not surprisingly breaks some tests. I undo my change and save, and the tests rerun and pass.

This is incredible to have during development cycles. I just put the terminal on the side where I see it in my periphery while coding.

Conclusion

I'm pretty happy with this new set of Javascript testing tools for working on eldarion-ajax. I'm now looking forward to the aggressive refactoring instead of being intimidated by it.

How are you handling testing your Javascript? I'd love to hear from you.