When it comes to writing automated tests, there can be a lot of challenges. For instance, your project runs JavaScript in a browser.

Unlike the “ease” of Node scripts, which can run from the command line, testing these browser functions usually involves opening a browser window where your test scripts and code that’s being tested are running.

Whenever “automated” test running requires a manual step like opening a browser, the frequency that the tests are run is in jeopardy. When unit tests are not run frequently and consistently, they lose their value.

Karma is a tool that helps us run these browser from the command line.

Prerequisites & Tools and topics covered

If you’re following along with tutorial you should have a basic understanding of managing Node projects using npm.

These are the tools we’ll be covering. I will not be going in depth to Mocha or Chai usage, just giving examples of how to set them up for this project. I highly recommend you review the docs on your own after this tutorial.

  • Mocha — testing framework
  • Chai — assertions library
  • Karma —command-line test runner

Let’s get started

First, let’s install Mocha.

$ npm install mocha --save-dev

To make sure we have things working, let’s add a basic test by adding a file called test/test.js and adding lines:

describe('Hello world', function(){
    it('should log "Hello world" to the console', function(){
        console.log('Hello world');
    });
});

Next thing we need to do to set this up is just add a script definition to our package.json file…

"scripts": {
    "test": "mocha"
}

…so that we can run the test like this

$ npm test
> karma-mocha-example@1.0.0 test /Users/jaz/github/karma-mocha-example
> mocha
Hello world
Hello world
✓ should log "Hello world" to the console
1 passing (8ms)

Good, we have our test running — but if you noticed, it’s not actually testing anything. Mocha is just a framework for defining and running tests, but we’ll need to use another library for the actual assertions.

There a lot of options, but for this example, let’s use Chai.


$ npm install chai --save-dev

Now we can use Chai to confirm behavior of our functions.

To add to the previous example, we’re going to add a basic assertion. Since this is just an example, I’m just going to test that the string “Hello world” has a length of 11.

const chai = require('chai');
const expect = chai.expect;
describe('Hello world', function(){
    it('should be 11 characters long', function(){
        expect( 'Hello world' ).to.have.length( 11 );
    });
});

So now when I run the tests,

$ npm test
> karma-mocha-example@1.0.0 test /Users/jaz/github/karma-mocha-example
> mocha
Hello world
✓ should be 11 characters long
1 passing (10ms)

Ok, we have both Mocha and Chai set up, but you may have noticed, we’re running this from the command line, NOT from a browser like we need to.

Getting setup to run browser

Let’s say I have this project

  • test/test.js is the test file we already added
  • js/helloworld.js contains the functions we want to test
  • index.html is where our functions are run

Let’s take a look at what our files have

index.html

<!doctype html>
<head>
    <title>Hello world!</title>
</head>
<body>
    <h1 class="greeting">
    </h1>

    <script src="js/helloworld.js"></script>
    greet();
</body>
</html>

js/helloworld.js

function helloWorld(){
    return 'Hello world';
}

function greet(){
    const heading = document.querySelector('.greeting');
    heading.innerHTML = helloWorld();
}

Let’s add our first test for helloWorld(). We can empty out our test/test.jsfile and add the following test:

describe('helloWorld()', function(){
    it('should return "Hello world"', function(){
        expect( helloWorld() ).to.be.equal( 'Hello world' );
    });
});

Now let’s run it

$ npm test
> karma-mocha-example@1.0.0 test /Users/jaz/github/karma-mocha-example
> mocha
helloWorld()
1) should return "Hello world"
0 passing (22ms)
1 failing
1) helloWorld()
should return "Hello world":
ReferenceError: helloWorld is not defined
at Context.<anonymous> (test/test.js:6:17)
npm ERR! Test failed.  See above for more details.

It failed because it couldn’t find the function called helloWorld. Let’s think about this. In our test script, we’re not actually requiring the file that contains that function. In the browser the function works because we include it as a <script> so we need to find a way to do that here.

Here’s where Karma comes in.

Setting up Karma to run tests

As always, we need to install some new node modules, but this time, not just Karma itself, we need the CLI as well.

$ npm install karma karma-cli --save-dev

To set up Karma, the CLI has a handy tool which will let you set up the config file by asking questions about our project. Here’s a guide to configuration options — feel free to experiment. I’ll focus on the bare-minimum that we need to get up and running.

$ karma init
Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> mocha
Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no
Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
>
What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> test/*.js
> js/*.js
>
Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
>
Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> no
Config file generated at "/Users/jaz/github/karma-mocha-example/karma.conf.js".

Now I have karma.conf.js (It will include a number of inline comments describing the configuration, I’m editing those out to make the code more compact).

// Karma configuration
module.exports = function(config) {
config.set({
  basePath: '',
  frameworks: ['mocha'],
  files: [
    'test/*.js',
    'js/*.js'
  ],
  exclude: [
  ],
  preprocessors: {
  },
  reporters: ['progress'],
  port: 9876,
  colors: true,
  logLevel: config.LOG_INFO,
  autoWatch: false,
  browsers: ['Chrome'],
  singleRun: false,
  concurrency: Infinity
})
}

One change I want to make before starting is setting singleRun to true so that when I try to run my test with Karma, it will quit after it tries to run.

We were previously running tests with Mocha when we were getting setup, but now we want to run with Karma, so we’ll need to change our test script

"scripts": {
    "test": "karma start karma.conf.js"
}

So now when we run our test script, Karma will launch, open Chrome, and run the tests.

$ npm test
> karma-mocha-example@1.0.0 test /Users/jaz/github/karma-mocha-example
> karma start karma.conf.js
10 09 2018 17:01:15.713:WARN [watcher]: All files matched by "/Users/jaz/github/karma-mocha-example/js/*.js" were excluded or matched by prior matchers.
10 09 2018 17:01:15.728:INFO [karma]: Karma v3.0.0 server started at http://0.0.0.0:9876/
10 09 2018 17:01:15.728:INFO [launcher]: Launching browser Chrome with unlimited concurrency
10 09 2018 17:01:15.734:INFO [launcher]: Starting browser Chrome
10 09 2018 17:01:17.117:INFO [Chrome 68.0.3440 (Mac OS X 10.13.6)]: Connected on socket MJqPgVeEmk4e1qGFAAAA with id 76779802
Chrome 68.0.3440 (Mac OS X 10.13.6) helloWorld() should return "Hello word" FAILED
ReferenceError: expect is not defined
at Context.<anonymous> (test/test.js:3:9)
Chrome 68.0.3440 (Mac OS X 10.13.6): Executed 1 of 1 (1 FAILED) ERROR (0.005 secs / 0 secs)
npm ERR! Test failed.  See above for more details.

It’s failing because it’s saying expect is not defined. Since expect is part of Chai, we need to install a plugin that Karma can use to interface with Chai.

$ npm install karma-chai --save-dev

Update karma.conf.js

frameworks: ['mocha', 'chai'],
plugins: ['karma-chai'],

Try running again…

$ npm test
> karma-mocha-example@1.0.0 test /Users/jaz/github/karma-mocha-example
> karma start karma.conf.js
10 09 2018 17:06:13.472:WARN [watcher]: All files matched by "/Users/jaz/github/karma-mocha-example/js/*.js" were excluded or matched by prior matchers.
10 09 2018 17:06:13.493:INFO [karma]: Karma v3.0.0 server started at http://0.0.0.0:9876/
10 09 2018 17:06:13.493:INFO [launcher]: Launching browser Chrome with unlimited concurrency
10 09 2018 17:06:13.502:INFO [launcher]: Starting browser Chrome
10 09 2018 17:06:14.834:INFO [Chrome 68.0.3440 (Mac OS X 10.13.6)]: Connected on socket fIYgCYbCfbXAsXIeAAAA with id 89651146
Chrome 68.0.3440 (Mac OS X 10.13.6): Executed 1 of 1 SUCCESS (0.003 secs / 0.001 secs)
TOTAL: 1 SUCCESS

Success! Our test ran and passed. But the output isn’t quite what I’d like — we see one test passes, but don’t get the same info we got with Mocha. Luckily, we can get that output by installing another plugin and adding it to our Karma configuration file.

$ npm install karma-mocha karma-mocha-reporter --save-dev

Update karma.conf.js

frameworks: ['mocha', 'chai'],
plugins: ['karma-chai', 'karma-mocha', 'karma-mocha-reporter'],
...
reporters: ['mocha']

At this point, for whatever reason, Karma is going to complain about not being able to launch Chrome, so let’s install the Chrome launcher.

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

Update config file…

plugins: ['karma-chai', 'karma-chrome-launcher', 'karma-mocha', 'karma-mocha-reporter'],

And now…

$ npm test
> karma-mocha-example@1.0.0 test /Users/jaz/github/karma-mocha-example
> karma start karma.conf.js
START:
10 09 2018 17:13:38.215:WARN [watcher]: All files matched by "/Users/jaz/github/karma-mocha-example/js/*.js" were excluded or matched by prior matchers.
10 09 2018 17:13:38.236:INFO [karma]: Karma v3.0.0 server started at http://0.0.0.0:9876/
10 09 2018 17:13:38.236:INFO [launcher]: Launching browser Chrome with unlimited concurrency
10 09 2018 17:13:38.253:INFO [launcher]: Starting browser Chrome
10 09 2018 17:13:39.569:INFO [Chrome 68.0.3440 (Mac OS X 10.13.6)]: Connected on socket lnPZAdaLy-_z7rSXAAAA with id 93623682
helloWorld()
✔ should return "Hello world"
Finished in 0.004 secs / 0.001 secs @ 17:13:39 GMT-0400 (EDT)
SUMMARY:
✔ 1 test completed

We get that nice summary of the tests run and what they do.

We have one more function in our js/helloworld.js that needs a test, so I recommend trying to add that one yourself.

See my github for the solution https://github.com/jazanne/karma-mocha-example

Hint: you’ll be testing the DOM so you’ll need to make sure it has any required elements.

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.