How to implement testing + code coverage on React with Karma, Babel, and Webpack

I’m writing this article because of the difficulties I faced when I was tasked with setting up testing on my companies React powered UI. If you’re looking for a step by step process to getting your testing running using Karma, and Karma’s coverage plugin, with Babel and Webpack all working nicely together and returning production code coverage metrics (rather than transpiled code), then read on.

Our Setup

We use React with Babel and Gulp so we can work with ES6 which presented it’s own set of unique problems when it came to getting test coverage (which I’ll get to in a minute). On the testing side, we ditched Jest in favor of Karma with Webpack.

I credit this post for the first 2/3rds of the initial Karma with Webpack setup that follows, but I’ve added some helpful links and command line calls to smooth out the installation process. It may seem like a lot of steps below, but that’s due to Karma’s modular nature. Before we start, I’m assuming you have React setup, if not, go do that then come back here.

Installs

Ready? Here we go:

  1. Install karma
npm install karma --save-dev

2. Install the karma cli so you can use karma in terminal

npm install karma-cli -g

3. Install the mocha test framework.

npm install mocha --save-dev

4. Install karma-mocha in order to use the mocha test framework with Karma

npm install karma-mocha --save-dev

5. Install karma-webpack which allows karma to work with webpack for preprocessing (and post-processing which we will need to get non-transpiled code from our test coverage tool (and all other sorts of processing)).

npm install --save-dev karma-webpack

6. Install karma-sourcemap-loader which corrects issues brought up by karma working alongside preprocessors like gulp or grunt.

npm install karma-sourcemap-loader --save-dev

7. Install karma-chrome-launcher which allows you to use the debugger in the browser while testing.

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

8. Install the expect assertion library

npm install expect.js --save-dev

9. Install babel-loader so you can use JSX and ES6

npm install babel-loader --save-dev

10. Install webpack where the magic happens.

npm install webpack --save-dev

11. Install karma-coverage, karma’s versitile test coverage plugin.

npm install karma karma-coverage --save-dev

12. Finally, install istanbul-instrumenter-loader, which is what fixes the issue with karma-coverage returning transpiled source code.

npm i -D istanbul-instrumenter-loader

Config

Now that we have all those installs out of the way, it’s time to setup our config files.

karma.conf.js (95% of the below code was originally posted here, with coverage additions being mine)

Oh, and don’t copy the code below. Medium forces curly quotes. Instead, I’ve created a repo with a useable copy of the config code here.

var webpack = require(‘webpack’);
module.exports = function (config) {
config.set({
browsers: [ ‘Chrome’ ], //run in Chrome
singleRun: true, //just run once by default
frameworks: [ ‘mocha’ ], //use the mocha test framework
files: [
‘tests.webpack.js’ //just load this file
],
plugins: [ ‘karma-chrome-launcher’, ‘karma-chai’, ‘karma-mocha’,
‘karma-sourcemap-loader’, ‘karma-webpack’, ‘karma-coverage’,
‘karma-mocha-reporter’
],
preprocessors: {
‘tests.webpack.js’: [ ‘webpack’, ‘sourcemap’ ] //preprocess with webpack and our sourcemap loader
},
reporters: [ ‘mocha’, ‘coverage’ ], //report results in this format
webpack: { //kind of a copy of your webpack config
devtool: ‘inline-source-map’, //just do inline source maps instead of the default
module: {
loaders: [
{ test: /\.js$/, loader: ‘babel-loader’ }
],
postLoaders: [ { //delays coverage til after tests are run, fixing transpiled source coverage error
test: /\.js$/,
exclude: /(test|node_modules|bower_components)\//,
loader: ‘istanbul-instrumenter’ } ]
}
},
webpackServer: {
noInfo: true //please don’t spam the console when running in karma!
},
coverageReporter: {
type: ‘html’, //produces a html document after code is run
dir: ‘coverage/’ //path to created html doc
}
});
};

Now we just need our tests.webpack.js file. (again, go to the repo to copy) This will find all of our tests and run them for us.

var context = require.context('./src', true, /-test\.js$/); //make sure you have your directory and regex test set correctly!
context.keys().forEach(context);

Finally, write a sample test like the one below.

var React = require('react');
var TestUtils = require('react/lib/ReactTestUtils'); //I like using the Test Utils, but you can just use the DOM API instead.
var expect = require('expect');
var Root = require('../root'); //my root-test lives in components/__tests__/, so this is how I require in my components.

describe('root', function () {
it('renders without problems', function () {
var root = TestUtils.renderIntoDocument(<Root/>);
expect(root).toExist();
});
});

This will do two things, first confirm that our actual tests are up and running, and second it will produce a new ‘coverage’ folder that will contain out code coverage html reports. Open the index.html file in your browser and confirm that your coverage metrics are being produced correctly. (probably 0% coverage right now :)

Conclusion

There you have it. You should now have a fully functioning testing suite. Best of luck.