To get Jasmine working with our code we'll set up a test runner called Karma. Remember that we can use Jasmine to write our tests but we need another package to actually run the tests. We could conduct our tests using Node in the command line (as we saw at the end of the last lesson), but there's still a problem: Jasmine doesn't recognize ES6 syntax. In other words, it won't work with the import and export statements we're using with webpack! We'll also be using a lot more ES6 syntax soon; for that reason, we need to be able to use Jasmine with ES6 features.

Introduction to Karma

Karma can solve this problem for us. Karma is a popular test-runner that can be used with many different testing frameworks, including Jasmine, Mocha, and more. Karma provides several key benefits as a test-runner:

  1. It helps us use newer ES6 (and beyond) features of JavaScript with Jasmine.

  2. It allows us to run tests continuously. Each time we save our code our tests will update, allowing us to quickly pinpoint bugs.

  3. Karma also 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 in depth, but you should know about it since testing different browsers is important for large commercial projects you may en.

  4. We can use plugins to extend Karma functionality. 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

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 [email protected] --save-dev

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

$ npm install [email protected] --save-dev

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

$ npm install [email protected] --save-dev

We’ll also need to add karma-cli. "CLI" stands for "command line interface". This package allows our Terminal to recognize Karma-specific commands. We'll first install it globally, like this:

$ npm install [email protected] -g

This will allow our Terminal to recognize Karma commands globally, from any Terminal location. Next, we'll also need to install this package directly in our codebase. (Make sure to run this command in the top-level of your project directory):

$ npm install [email protected] --save-dev

In addition, we need to install a package so that Karma can work with webpack:

$ npm install [email protected] --save-dev

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

$ npm install [email protected] --save-dev

We’ll also want to make our testing report easy on the eye. This package helps format our test results in a much more human-readable way:

$ npm install [email protected] --save-dev

Finally, when Karma runs our code, it also runs webpack but does not automatically use source mapping. If we want to see correct stack traces while using Karma, we need to install another package called karma-sourcemap-loader. We'll save this to the list of development dependencies and not to the list of production dependencies since we only need it for testing.

$ npm install [email protected] --save-dev

Now it’s time to initialize Karma by running the following in the root directory of your project:

$ karma init

You’ll be prompted to answer a series of questions. Go ahead and hit Enter until the prompts are finished. If you receive a bash error after running this command, this could be because your machine doesn't recognize the karma command. Double-check that you've successfully installed Karma CLI globally with $ npm install -g karma-cli, as mentioned above. You may need to quit and re-start your Terminal.

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
const webpackConfig = require('./webpack.config.js');

module.exports = function(config) {
  config.set({
    basePath: '',
    frameworks: ['jquery-3.2.1', 'jasmine'],
    files: [
      'src/*.js',
      'spec/*spec.js'
    ],
    webpack: webpackConfig,
    exclude: [
    ],
    preprocessors: {
      'src/*.js': ['webpack', 'sourcemap'],
      'spec/*spec.js': ['webpack', 'sourcemap']
    },
    plugins: [
      'karma-jquery',
      'karma-webpack',
      'karma-jasmine',
      'karma-chrome-launcher',
      'karma-jasmine-html-reporter',
      'karma-sourcemap-loader'
    ],
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false,
    concurrency: Infinity
  })
}

(If you wish, you can copy and paste this configuration directly into your karma.conf.js file.)

Let’s focus on the options that have been customized and updated:

  • frameworks specifies the frameworks that Karma should recognize and use. We want Karma to understand both jQuery and Jasmine.

  • The files array lets Karma know which files to load. Specifically, we need to load our JavaScript business logic 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 beginning 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). It's included here to emphasize that the included functionality we’re about to discuss comes from plugins.

A Karma configuration can also include plugins for preprocessors, frameworks, reporters, and browsers.

  • karma-jquery 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.

  • A preprocessor processes files before the browser serves them. In this case, we're running our files through webpack; otherwise they won’t work. We also need to require our webpack configuration so that our Karma file knows how to use it. We also need to indicate that when webpack "preprocesses" our code for testing, it should use the sourcemapping tool we've set up in webpack. The reason sourcemapping isn't included by default is because it is labor intensive and will slow down the execution of our tests. However, it will be helpful for debugging our tests.

  • 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 to make our test command point to Karma. To use Karma to run our tests we need to tell npm exactly where the Karma CLI commands are held. Our installation path for karma-cli is ./nodemodules/karma/bin/karma. The command for running tests is start. We also need to dictate which file to use for our Karma configuration. So, the complete command for running our tests is `> ./nodemodules/karma/bin/karma start karma.conf.js. Let's set ourtest` command to this:

package.json
...
  "scripts": {
    "test": "./node_modules/karma/bin/karma start karma.conf.js"
  },
...

Now, when we run $ npm test, Karma will launch a Chrome browser. However, we haven't written any tests yet. (We'll do that in the next lesson.)

Excluding Specs from ESLint

There's one other thing we need to fix. Our linter will get mad at us because it doesn't understand Jasmine syntax. We don't want to lint our tests anyway! This involves a small change to webpack.config.js:

webpack.config.js
...
module.exports = {
  ...
      {
        test: /\.js$/,
        exclude: [
          /node_modules/,
          /spec/
        ],
        loader: "eslint-loader"
      }
    ]
  }
};

We just update the exclude section for our ESLint loader so that it also excludes our spec files. Note that we're now using an array because we have multiple things we need to exclude.

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.

Now it's time to actually start writing tests.