Karma JS Testing: What, Why, and How to Get Going Right Now

Automated testing is a must for any modern software development organization. That's why you must know and master tools in…

Testim
By Testim,

Automated testing is a must for any modern software development organization. That’s why you must know and master tools in that space if you have any hope of producing high-quality code. Fortunately, there is a huge variety of testing tools available. These tools cater to virtually every need of a comprehensive testing strategy. When it comes to tools used in Angular unit testing—or even vanilla JavaScript testing, for that matter—no tool is better known than Karma (sometimes also called “Karma JS.”)

Karma is a testing tool developed by the AngularJS team, so it’s understandable why it’s usually associated with the framework. However, there’s nothing preventing you from using Karma with regular JavaScript apps.

In today’s post, we offer a guide to testing with Karma. We start with definitions, by explaining what Karma is, why you would want to use it, and which role it plays in the vibrant—and sometimes a bit chaotic—JavaScript testing landscape.

Then we advance to the more hands-on part, where you’ll learn how to install and configure Karma. Before wrapping up, we share some final considerations and additional tips. Let’s begin!

Karma JS Fundamentals

Let’s start with the most basic questions. What is Karma? Why should you care? That’s what we’re going to see in this section.

Defining Karma

Before we explain what Karma is, it makes sense to explain what it’s not.

Karma isn’t a testing framework. So, it isn’t a competitor to tools like Mocha, QUnit, or Jasmine, to name a few. Instead, Karma works with them, so you’ll have to pick a testing framework to use. There are available plugins for the most well-known testing frameworks. Alternatively, you can also write an adapter for your favorite testing framework, if there isn’t one already.

Another thing that Karma isn’t is an assertion library. So, tools like Chai or Should.js aren’t competitors to Karma.

So, what is Karma after all? Karma is a test runner. But why do you need it?

Why Karma?

You might be wondering what’s the point of using Karma. Since you’d still need a testing framework, what’s the point of using an additional tool? What’s the value added by Karma?

The main selling point of Karma is the ability to run tests against real browsers and real devices, which makes your testing strategy more robust and reliable. Since different browsers can have different DOM implementations, testing against all of them—or at least most of the major ones—is essential if you want to ensure your application will behave properly for the majority of its users.

Besides, we live in an era of not only plenty of browsers but also plenty of devices. Phones, tablets, you name it. Karma also allows you to run tests against them.

Karma also makes it easy for developers to continuously run their tests without leaving their terminal or IDEs since it can re-run the tests every time the files change.

Getting Started With Karma

With the basics out of the way, we’re ready to roll-up our sleeves and start doing some work. Let’s start by installing Karma.

Installing

We’ll use npm to install Karma. That means you must have Node.js installed. If you don’t already have it, please download it and install it before progressing through the guide.

Installing Karma is super easy. First, we’re going to show you how to install it globally (though that’s not the recommended approach). Just run the following command:

npm install -g karma

By installing Karma globally, you’ll have access to the “karma” command no matter your current location. To verify whether the installation was successful, just run “karma –version” and you should see the current version number.

However, as we’ve just mentioned, the global installation isn’t the recommended approach, and we’ve just mentioned it for completion’s sake. What you should do instead is to install Karma locally for each project. You do that by running the following command from your project’s directory:

npm install karma --save-dev

You’re also going to need specific plugins, depending on the other testing tools you use. For instance, the following line installs the karma-mocha plugin (required to use the Mocha testing framework), and the karma-chrome-launcher and karma-firefox-launcher plugins (necessary to test against Chrome and Firefox.) Finally, the plugins are all saved as devDependencies in the packages.json configuration file.

npm install karma-mocha karma-chrome-launcher karma-firefox-launcher --save-dev

Running

To run Karma, you will need to run the following command, from your project’s root folder:

./node_modules/karma/bin/karma start

Writing the complete path above gets old really quick, though. Instead of doing that, you might want to install the karma-cli tool globally, by running this:

npm install -g karma-cli

With that installed, you’ll be able to just run “karma” no matter your current location, and the local version will run.

Configuring

You configure Karma via a config file. It’s possible to create the config file either by hand or by using the “karma init” command and answering the questions asked. Alternatively, you could also just copy a config file from a different project.

You can write the configuration file using JavaScript, CoffeeScript, or TypeScript. When running Karma, you can provide the path to the configuration file as an argument. If you don’t do so, the Karma CLI will look for a configuration file which matches one of the following names:

./karma.conf.js
./karma.conf.coffee
./karma.conf.ts
./.config/karma.conf.js
./.config/karma.conf.coffee
./.config/karma.conf.ts

When you generate the config file by running “karma init,” you’re asked several questions. Here’s an example of the questions along with their answers:

Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> jasmine

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 quest
ion.
> 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.
> js/*.js
15 01 2020 01:09:59.640:WARN [init]: There is no file matching this pattern.

> test/*Spec.js
15 01 2020 01:10:15.087:WARN [init]: There is no file matching this pattern.

>

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.
> yes

With the answers above, we inform Karma, among other things:

  • Jasmine will be our testing framework
  • where our tests are located, and our production code as well
  • we want Chrome to be automatically captured
  • we want Karma to watch the files and run the tests again as they change

The resulting config file should look like this:

// Karma configuration
// Generated on Mon Dec 06 2021 09:35:56 GMT-0300 (GMT-03:00)

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: '',


    // frameworks to use
    // available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter frameworks: ['jasmine'],


    // list of files / patterns to load in the browser
    files: [
    ],


    // list of files / patterns to exclude
    exclude: [
    ],


    // preprocess matching files before serving them to the browser
    // available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor preprocessors: {
    },


    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://www.npmjs.com/search?q=keywords:karma-reporter reporters: ['progress'],


    // web server port
    port: 9876,


    // enable / disable colors in the output (reporters and logs)
    colors: true,


    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,


    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: true,


    // start these browsers
    // available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher browsers: ['Chrome'],


    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: false,

    // Concurrency level
    // how many browser instances should be started simultaneously concurrency: Infinity
  })
}

With this configuration done, you should be able to run “karma start” and see a new Chrome window like the one below:

karma v 6.3.9

Testing an Application Using Karma

Let’s now use Karma to test a super simple app written with vanilla JavaScript. Create a new folder. In it, create a new file called index.html and paste the following content in it:

<!DOCTYPE html>
<html>
<head>
    <title>String Calculator Kata</title>
</head>
<body>
<input id="input-numbers" type="text">
<input id="add-btn" type="button" value="Add">
<div><span id="result" /></div>
<script type="text/javascript" src="string-calculator.js"></script>
<script type="text/javascript">
    init();
</script>
</body>
</html>

Create a new file called string-calculator.js with the following content:

const calculate = () => {
    const usrInput = document.getElementById('input-numbers');
    const resultElement = document.getElementById('result');
    const text = usrInput.value;
    const numbers = text.split(',').map(x => parseInt(x));
    const negatives = numbers.filter(x => x < 0);
    let result;

    if (negatives.length > 0) {
        result = `Negatives not allowed:${negatives.join(', ')}`;
    } else {
        result = numbers
            .filter(x => x <= 1000)
            .reduce((a, b) => a + b, 0);
    }

    resultElement.textContent = result;
}

function init() {
    const button = document.getElementById('add-btn');
    button.addEventListener('click', calculate);
}

Execute the file locally. This application is based on the String Calculator Kata, a programming exercise proposed by Roy Osherov. The idea is simple: to have a function that receives a string containing a list of numbers separated by comma and returns their sum:

karma js 123

There are a few rules:

  • Numbers greater than 1000 should be ignored:karma greater than 1000
  • Negative numbers aren’t allowed: they should be listed in a message:karma not allowed

Installing Karma and Dependencies

Go to your terminal, access the project’s root folder and run the following command:

npm install karma --save-dev

As you’ve seen, the command above installs Karma as a development dependency for the project.

Then, run the following:

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

The command above installs the plugin we’ll need to work with Jasmine and the launcher for Google Chrome.

Configuring Karma With Karma Init

Let’s now configure Karma. Run karma init. Then, let’s answer the questions one by one:

Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> jasmine

There are many unit testing frameworks for JavaScript, and Karma can work with several of them. For this tutorial, let’s go with Jasmine.

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

Choose no for this question, as we’ll not need Require.js.

Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
>

For this question, pick only Chrome, since we want a very simple setup.

What is the location of your source and test files ?
> *.js
> *.test.js

For this question, you’ll enter the patterns above.

Should any of the files included by the previous patterns be excluded ?

Leave the question above empty.

Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes

It’s very convenient to have Karma rerun the tests automatically after every change, so pick yes.

Installing Jasmine

The next step is installing Jasmine itself:

npm install jasmine --save-dev

Writing Your First Test

Inside the root of the project, create a new file called string-calculator.test.js. It should have the following content:

describe('String Calculator', function() {
    // 1
    beforeEach(function() {
      var fixture = `<div id="fixture">
      <input id="input-numbers" type="text">
        <input id="add-btn" type="button" value="Add">
        <div><span id="result" /></div>
    </div>`;
      document.body.insertAdjacentHTML(
        'afterbegin',
        fixture);
    });
    

    afterEach(function() {
      document.body.removeChild(document.getElementById('fixture'));
    });
    // 2
    beforeEach(function() {
      init();
    });
    // 3
    it('should return 0 for empty string', function() {
        document.getElementById('input-numbers').value = '';
        document.getElementById('add-btn').click();
        expect(document.getElementById('result').innerHTML).toBe('0');
      });

    it('should return 6 for 1,2,3', function() {
      document.getElementById('input-numbers').value = '1,2,3';
      document.getElementById('add-btn').click();
      expect(document.getElementById('result').innerHTML).toBe('6');
    });
  });

We’ll now explain the code above, using the numbered sections:

  1. We start by adding a fixture that simulates the HTML code of our page.
  2. Before each test execution, we call the init function defined in the app.js file. That’s necessary to add the event listener to the button.
  3. Finally, we have the tests themselves. The first test checks the method returns 0 when passed an empty string. The second one verifies whether a string containing “1,2,3” really results in 6.

You’re now ready to run the test. Go back to your terminal and run:

karma start

Your terminal will display the results of the test run:

07 12 2021 00:54:02.680:INFO [karma-server]: Karma v6.3.9 server started at http://localhost:9876/
07 12 2021 00:54:02.681:INFO [launcher]: Launching browsers Chrome with concurrency unlimited
07 12 2021 00:54:02.686:INFO [launcher]: Starting browser Chrome
07 12 2021 00:54:02.875:INFO [Edge 96.0.1054.43 (Windows 10)]: Connected on socket Jer15qacnk_Him78AAAB with id manual-2743
07 12 2021 00:54:03.857:INFO [Chrome 96.0.4664.45 (Windows 10)]: Connected on socket mlN6zQD1mhPhL2qNAAAD with id 71193920
Chrome 96.0.4664.45 (Windows 10): Executed 2 of 2 SUCCESS (0.038 secs / 0.008 secs)
Edge 96.0.1054.43 (Windows 10): Executed 2 of 2 SUCCESS (0.27 secs / 0.048 secs)
TOTAL: 4 SUCCESS

Congrats! You’ve successfully written and run your first test with Karma and Jasmine!

What Now?

As pointed out, Karma is neither a testing framework nor an assertion library. You don’t write tests using Karma. Instead, you use the testing framework you already know and love, be it Mocha, Jasmine, or others. What Karma will do is to automatically run your tests every time the files change, ensuring they work against a variety of browsers and devices.

By following the examples in this post, you should by now have a template for running your tests with Karma.

What are the next steps for you? In short, play and tweak with the basic setup we’ve got here. For instance, the config file of our example uses Jasmine. Configure Karma to use Mocha or another framework. Also, there is life beyond Chrome! Test using browsers others than Chrome.

Finally, stay tuned for more JavaScript-related QA content right here on the Testim blog. Thanks for reading, and until next time.

This post was written by Carlos Schults. Carlos is a .NET software developer with experience in both desktop and web development, and he’s now trying his hand at mobile. He has a passion for writing clean and concise code, and he’s interested in practices that help you improve app health, such as code review, automated testing, and continuous build.