Unit testing JavaScript with Karma in 2021. Starting point 🌻

Maksim Ryzhikov
6 min readJan 30, 2021

--

Karma was born in the era of AngularJS. This was a significant improvement of existed test tooling on the Web and was one of the reasons for the spread of TDD in the JavaScript community. Karma still the best test runner for web JavaScript after almost 10 years thanks to the architecture and principles it’s based on.

Let's set up it for a test project and try to understand how it works. The first step is to install it via NPM

npm install --save-dev karma

After successful installation, we would have Karma binary inside the local node_modules folder. Now let’s create the Karma configuration file for our project using the init command

npx karma init

Here we could select the testing framework from the list of popular frameworks like “Jasmine”, “Mocha” etc. In our example, we would use “Jasmine” so just press [Enter] to move to the next question.

Do you want to use Require.js ?
no

Require.js was popular 10 years ago for management dependencies when we didn’t have Webpack. Now it’s mostly rudiment so here we select [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

We would use Chrome as a default browser for our tests because this is one of the popular browsers nowadays and works on most of operating systems

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.
> src/**/*.test.js
> src/**/*.js

Here we should tell Karma where we keep our source and test files. In our example, we would use src/**/*.test.js patter for test files which are widespread now, and src/**/*.js for source files

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

Leaves it empty

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

Karma can be run in two modes “single run” or “watch”. Single run means that when tests are finished karma would stop and to rerun our tests we should do run tests manually again. In the “watch” mode Karma would automatically rerun our tests when we change either the source or test files. This pretty handy feature saves our time, so we select [yes] here

Config file generated at "/<project>/karma.conf.js".

We have finished the configuration and Karma says that the result is a karma.conf.js file. But this is not all. Karma also has installed necessary dependencies based on our choices and updated package.json

+    "karma-chrome-launcher": "^3.1.0",
+ "karma-jasmine": "^4.0.1",

In our case these were

  • karma-chrome-launcher the plugin which helps run tests in Chrome
  • karma-jasmine an adapter for jasmine

Karma has a plugin architecture. Most of the language and frameworks specific functionality provided by plugins. Karma itself as a runner provides only functionality to run arbitrary JavaScript in a browser via Karma HTTP server. Also, Karma gives you the possibility to process your test and source files before they would be sent to the browser. So roughly say Karma is a mediator between your test/source files, frameworks, browsers, and operating system. Thus, this architecture allows karma to do only one thing but do it well and be open for extension without changing the Karma source itself. I highly encourage you to watch this video and look at the Karma source code itself as an example of a good product that stood the test of time.

Let’s move back to our test project and finish the Karma setup. Because we decided to use Jasmine as a test framework we should install it too.

npm install jasmine

karma-jasmine only a Karma adapter for Jasmine itself so this is why we should install Jasmine alongside karma-jasmine.

Now we are ready to write our first test. Let’s create a file src/foo.test.js and write a test case

describe('My first Karma test', function() {
it('should be true', function() {
expect(true).toEqual(true);
});
});

Before start Karma make sure that you have installed Chrome which we select as our default browser for tests. Now we are ready to run our tests

npx karma start

This command would start the Karma server, open the Chrome browser, and run our tests. The result of tests would be printed in the terminal

30 01 2021 14:59:09.329:WARN [karma]: No captured browser, open http://localhost:9876/
30 01 2021 14:59:09.353:INFO [karma-server]: Karma v6.0.3 server started at http://localhost:9876/
30 01 2021 14:59:09.353:INFO [launcher]: Launching browsers Chrome with concurrency unlimited
30 01 2021 14:59:09.358:INFO [launcher]: Starting browser Chrome
30 01 2021 14:59:10.799:INFO [Chrome 88.0.4324.96 (Mac OS 11.1.0)]: Connected on socket kj6wu3FYg7XPKEKCAAAB with id 15222593
Chrome 88.0.4324.96 (Mac OS 11.1.0): Executed 1 of 1 SUCCESS (0.002 secs / 0.002 secs)
TOTAL: 1 SUCCESS

Here you can notice several interesting points

  1. Karma starts the server
  2. Open http://localhost:9876 in Chrome
  3. Run tests and print the result

When tests finished Karma would not stop and wait for changes to rerun tests automatically. Let’s change our test to see how it works

it('should be true', function() {
expect(true).toEqual(false);
});

And now look at the result

30 01 2021 15:02:37.443:INFO [filelist]: Changed file "/playground/src/foo.test.js".
Chrome 88.0.4324.96 (Mac OS 11.1.0) My first Karma test should be true FAILED
Error: Expected true to equal false.
at <Jasmine>
at UserContext.<anonymous> (src/foo.test.js:3:18)
at <Jasmine>
Chrome 88.0.4324.96 (Mac OS 11.1.0): Executed 1 of 1 (1 FAILED) (6.226 secs / 0.001 secs)
TOTAL: 1 FAILED, 0 SUCCESS

You can see that Karma automatically rerun the test and told us that now we have 1 failed test. Also, the result contains useful information about the error. The error message contains

  • Name of a test suite. In our case My first Karma test
  • Name of a test case — test should be true
  • Expectation/assert error message — Expected true to equal false
  • Exception stack trace

Because Karma runs tests inside a browser you can use the browser’s developer tools to debug your tests. For this purpose, you can set a breakpoint in a test and open developer tools in Chrome. Also, you can open http://localhost:9876/debug page where tests would be run in the main window and don’t produce the result in the terminal. If you want to run your tests in another browser you can easily do it without stopping tests and changing configuration by open http://localhost:9876/ in an interesting browser. It would attach the browser to the Karma server and run tests on it too. This approach also allows you to run tests on any device and any browser just by opening Karma server URL.

Ok. Let’s fix our test by reverting to the previous change. To stop tests press [Ctrl-C] in the terminal where Karma is running. Now let’s change the Karma configuration file so that test runs without “watch” by default. To do this open karma.conf.js and change singleRun from false on true

singleRun: true,

Now if we run tests again by npx karma start you can see Karma stops after tests finished. To run tests in watch mode we should pass --no-single-run parameter

npx karma start --no-single-run

When karma running in watch mode you can rerun tests without changing source and test files by run command. To do this we should open a new terminal window without terminating karma start process and run

npx karma run

This command mostly useful for the integration of different tools with Karma.

Now let’s talk about source files. In the example above we have added the test file that contains all logic inside itself. Let’s create the file src/foo.js and write function multiply

function multiply(x, y) {
return x * y;
}

now let’s change add test inside src/foo.test.js so that it would check the result of function multiply

it('should multiply 2 on 2', function() {
expect(multiply(2, 2)).toEqual(4);
});

If you don’t stop Karma server you can see that it automatically detect new file and test

Chrome 88.0.4324.96 (Mac OS 11.1.0): Executed 2 of 2 SUCCESS (0.006 secs / 0 secs)
TOTAL: 2 SUCCESS

Now we have two tests. You can see that function multiply available in the test file this is because all files are loaded and executed in the same context (on the same page). We had not restricted visibility of our function multiply thus it’s available globally, and we can use it in our test.

Here I finish the article. This is a basic setup of Karma and starting point for advanced configuration. I recommend starting from the basic setup and when you are sure that it works correctly move towards advanced configuration which usually includes preprocessing step for web projects in our days.

--

--

No responses yet