Lesson Monday

Note: This lesson assumes you’ve fully set up your gulpfile.js for your project. If you haven’t and you’d like to try out Karma on its own with Jasmine, you’ll need to run the following commands: $ npm install browserify --save-dev and $ npm install watchify --save-dev.

Karma is a popular test-runner that can be used with many different testing frameworks, including Jasmine, Mocha, and other testing libraries. Karma provides several key benefits as a test-runner over Jasmine’s SpecRunner.html file.

First, Karma allows us to run our tests continuously. Each time we save our code, our tests will update, which allows us to quickly pinpoint bugs.

Second, Karma executes our application’s source code in the browser and then runs it against our test code. We can use this functionality to check if our code is compatible with different browsers. For instance, what works in Chrome might not work in Internet Explorer. We won’t cover this feature in too much depth, but at the very least, you should know about it, as testing against different browsers is very important for larger commercial projects. Browser compatibility is a huge pain point in the world of JavaScript development.

Third, we can use plugins to extend the functionality of Karma. This allows us to create a highly-customized testing environment that has all the tools we need.

While Karma itself is easy to use, configuration can be a bit tricky, especially if we have a unique use case for our applications. Node modules are constantly updating and evolving, and there are often multiple modules that have the same functionality. Some work well, others don’t, and documentation can be spotty.

Installing Karma

With that in mind, let’s set up Karma. Take the time to input each of these commands the first time you go through this lesson. You can reuse your completed package.json file for future projects. (Don’t forget to run $ npm install.)

First, let’s install Karma itself.

$ npm install karma --save-dev

Next, we’ll need to add some packages so that Karma and Jasmine can work together. (Note: it’s assumed that you’ve already installed Jasmine from the last lesson. If you haven’t, you can do so now.)

$ npm install karma-jasmine jasmine-core --save-dev

We also need to specify which browser (or browsers) we want Karma to launch. We’ll install the Chrome launcher. In a real-world environment, you’d likely want to test other browsers, too.

$ npm install karma-chrome-launcher --save-dev

We’ll also install karma-cli, which allows us to use Karma from the command line (CLI stands for Command Line Interface). This includes installing karma-cli globally with the following command: $ npm install -g karma-cli. You will only need to do this once if you’re working on your personal machine. You’ll also need to add karma-cli to your package.json:

$ npm install karma-cli --save-dev

In addition, we need Karma to browserify files, since the browser can't understand require statements and our code is separated into different modules:

$ npm install karma-browserify --save-dev

Karma doesn’t understand jQuery on its own, so we need a plugin for that, too:

$ npm install karma-jquery --save-dev

Last but not least, we’ll want to make our testing report easy on the eye:

$ npm install karma-jasmine-html-reporter --save-dev

That’s a lot of Node packages! Now It’s time to initialize Karma by running $ karma init in the root directory of your project. You’ll be prompted to answer a series of questions. Go ahead and hit Enter until the prompts are finished.

Configuring Karma

While we could input our source and test files in the command line (as well as other configuration options), we can just as easily configure these options inside the newly-generated karma.conf.js file inside our project. Let’s take care of that, along with all other necessary configuration:

karma.conf.js
module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['jquery-3.2.1', 'jasmine', 'browserify'],
    files: [
      'js/*.js',
      'spec/*-spec.js'
    ],
    exclude: [
    ],
    preprocessors: {
      'js/*.js': [ 'browserify'],
      'spec/*.js': ['browserify']
    },
    plugins: [
      'karma-jquery',
      'karma-browserify',
      'karma-jasmine',
      'karma-chrome-launcher',
      'karma-jasmine-html-reporter'
    ],

    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false,
    concurrency: Infinity
  })
}

Let’s focus on the options that have been customized and updated. We’ve updated the files array so Karma knows which files to load. Specifically, we need to load our backend JavaScript code as well as our tests. We use a globbing pattern (the * symbol) to specify that all files ending in .js and all files ending in -spec.js should be loaded.

We’ve also added a plugins array. Technically, Karma autoloads all “sibling” modules that begin with karma, so we don’t actually need this array to make our code work. However, there are benefits to explicitly stating the plugins being used (including enhanced readability). The only reason it’s included here is to emphasize that the included functionality we’re about to discuss comes from plugins.

Our configuration includes plugins for preprocessors, frameworks, reporters, and browsers.

karma-jquery, karma-browserify and karma-jasmine are plugins for the frameworks we’re using inside Karma. For instance, Jasmine is a testing framework while jQuery is a feature library that enhances the functionality of JavaScript.

We’ll also be using karma-browserify as a preprocessor. A preprocessor processes files before the browser serves them. In this case, we need to browserify our files first; otherwise they won’t work! We can specify which files we’d like to pre-process and we can specify the tools we’ll use as preprocessors by putting them in an array, just as we do here: 'js/*.js': [ 'browserify'].

karma-chrome-launcher is a plugin that we need to specify in browsers. We could test other browsers by adding their launchers to package.json and then specifying them here.

karma-jasmine-html-reporter is an example of a reporter. Reporters give us additional information about our tests. This reporter will give us a report in the browser that’s easy to read.

If you choose to customize Karma further, you’ll most likely do so with plugins. You may want to do some research on your own to see what other tools you can add. If you’d like to practice on your own, try adding Istanbul.js, a library that helps you track how well you’ve tested your code.

We’re almost ready to run our tests through Karma! There’s one last thing to do. $ npm test is still pointing at Jasmine. We need to update package.json one more time:

package.json
...
  "scripts": {
    "test": "karma start karma.conf.js"
  },
…

Now, when we run $ npm test, Karma will launch a Chrome browser. If you check the terminal, you’ll see that the tests were executed. Click the Debug button in the browser and you’ll get the results of your tests:

Jasmine shows five specs passing in the browser.

If any tests are failing, you’ll get a backtrace. Best of all, your tests will run continuously in the terminal until you choose to stop them (you can do so with Ctrl + C). At any time, you can refresh http://localhost:9876/debug.html and see the latest results of your tests.

We’ve only scratched the surface of what we can do with testing in JavaScript, but there’s more than enough to get started. If you’re interested, you can also explore e2e testing (integration testing) with tools like Protractor.js (developed for Angular) and Nightwatch.js. e2e is short for end to end; testing e2e means writing integrated testing that actually tests how your code runs in the browser with user input. Regardless of whether you explore e2e testing, you should fully unit-test your JavaScript applications.