Efficient TDD with Karma and Webpack

Utilising watch mode to speed up writing front-end unit tests

Posted by Josh Hale on January 22, 2020

Here at Bamboo, tests are an absolutely crucial part of our software system. Whenever possible, we try to make sure our Pull Requests are accompanied by relevant tests. Ideally, the act of writing a unit test, running the test and seeing feedback via the outputted results should be a fast, iterative process for the engineer. This post will highlight how we have acheived this with our front-end JavaScript tests using Webpack and Karma.

We start by taking a look at a high level overview of how our testing setup works and then we’ll dig down into some example code.

Our setup for writing front-end unit tests

We write JavaScript tests which are compiled by Webpack into assets which are served up to the browser via Karma, where they are then executed.

We run Webpack in watch mode which means any changes made to the tests or dependent JavaScript will cause a recompilation to occur. This recompilation will generate a new file containing tests to be executed. Karma will see that a new file has been generated and will automatically serve it to the browser, this is due to setting the autoWatch flag to true.

The diagram below shows how our front-end unit testing infrastructure is laid out.

image

A closer look at the code

I will step through a minimal example below which is setup to run ReactJS unit tests.

First, clone our example repository and install the dependencies.

# Clone the repository
git clone git@github.com:bambooengineering/example-karma-webpack-tdd.git
cd example-karma-webpack-unit-tests
yarn install

Now that the dependencies are installed, you can run yarn run test-server in the root of your project. This will use Concurrently to start 2 processes: Webpack and Karma, whilst piping the output of each to the terminal.

The first process that is started is Webpack:

node_modules/.bin/webpack --watch --config webpack.tdd.js

The --watch flag tells the Webpack process to stay alive and watch for any changes in the tree of imported files starting from our entry point, spec_helper.js. If any changes are made to the JavaScript to be tested e.g. example-karma-webpack-tdd/src/Title.js then Webpack will recompile the bundle. It’s worth noting that Webpack uses caching internally and so the recompilation is extremely performant, even with large bundle sizes.

// example-karma-webpack-tdd/webpack.tdd.js

module.exports = {
  entry: resolve(__dirname, 'spec_helper.js'), // Watch for changes in any of the dependencies of spec_helper.js.
  output: {
    path: resolve(__dirname, 'tmp'), // Output the compiled test code to the tmp directory.
    filename: 'spec_bundle.js' // Name the outputted file spec_bundle.js.
  },
  ...
}

The second process that is started is the Karma server:

node_modules/.bin/karma start

This process will look for a karma.conf.js file found in our project root. In this config file we tell Karma to serve the files found in tmp/spec_bundle.js, which is the file outputted by Webpack. We tell Karma to watch for any changes to this bundle file by setting the autoWatch: true flag.

Karma will launch Chrome automatically for us since we are using the karma-chrome-launcher.

// example-karma-webpack-tdd/karma.conf.js

module.exports = function(config) {
  config.set({
    browsers: ['Chrome'], // Use Chrome as the browser to execute tests in.
    files: [resolve(__dirname, 'tmp/spec_bundle.js')], // Load the file outputted by Webpack.
    autoWatch: true // Automatically serve the new test files to the browser when a change is detected.
    ...
  })
};

Once Chrome has started up, Karma will serve the files to Chrome and establish a WebSocket connection, in order to tell the browser page to reload when a new change has been made to one of our tests.

With Webpack, Karma and Chrome successfully running, we can modify our unit tests and be given rapid feedback on any assertion errors. The feedback (from both Webpack and Karma) is printed to the terminal used to launch the processes. The Chrome DevTools console can be used to debug any unexpected errors. Place a breakpoint or debugger in your JavaScript code and open the DevTools console in the same page your tests are being executed.

To see how quick the feedback mechanism is, try changing something in src/Title.js such that the test fails, revert the change and watch how fast the 2 processes work together in order to provide fast, effective feedback.

In this post we’ve shown how we use Webpack and Karma to efficiently run our JavaScript tests, and give fast feedback when using a TDD development practice.



Interested in joining our team? We're hiring! Contact us at