Posted on May 11, 2015 by Chris Harrington

Testing a React project with Karma and Webpack

I’m a new fan of Webpack. It’s a great tool for intelligently bundling all of the packages your project needs and its watch feature is super quick. I’ve been playing around with it a bunch for my latest project which uses React (see here: Webpack with LESS and React) so I figure it was about time for me to start writing some unit tests to back up all that fancy code. I did a quick search for running Jasmine unit tests via Gulp that utilize Webpack’s bundling while also allowing for transformations of JSX files into raw JavaScript, but I didn’t have much luck there. I quickly settled on a Karma implementation, as there’s a plugin built especially for that purpose.

The app that I’m building at the moment has a need for user profile images, and as part of the first version, I’m just defaulting to a Gravatar image. I figured this was a good candidate for a React class, so I built one. This article will describe how to test it.

What do I need?

Here’s the list of npm packages I’m using to get this up and running, in no particular order.

  • karma
  • karma-jasmine – the Jasmine provider so that Karma can understand the results of our tests.
  • karma-cli – allows us to run karma start from the command line.
  • karma-chrome-launcher – opens up an instance of Chrome when we run the tests.
  • karma-webpack – the plugin that allows us to bundle up our Webpack-ready source files to be used in specs.

You can install almost all of these with one command as follows:

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

The command line interface for Karma needs to be installed globally.

npm install karma-cli -g

Now what?

I’m not intending this article to be a tutorial for getting started with Karma, so I’m just going to throw up the config I’m using as an example. For further instruction on Karma in general, check out their official documentation. The Karma config file can be generated using the karma init command, which asks you a bunch of questions and then writes the answers to those questions into a JavaScript file. I made some modifications to the generated code, which I’ll go over shortly. For reference, here’s what my project structure looks like:

karma.conf.js
src
  components
    gravatar.js
test
  specs
    components
      gravatar.test.js

And here’s the Karma configuration I’m using.

var webpack = require("webpack"),
	path = require("path");

// Karma configuration
// Generated on Mon May 11 2015 14:13:57 GMT-0600 (MDT)

module.exports = function(config) {
  config.set({
    basePath: "",
    frameworks: ["jasmine"],
    files: [
		"./test/**/*.test.js"
    ],
    preprocessors: {
		"./test/**/*.test.js": ["webpack"]
    },
	webpack: {
		module: {
			loaders: [
				{ test: /\.js$/, loader: "jsx-loader" },
				{ test: /\.less$/, loader: "style!css!less" }
			]
		},
		plugins: [
			new webpack.ResolverPlugin([
				new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin("bower.json", ["main"])
			])
		],
		resolve: {
			root: [path.join(__dirname, "./bower_components"), path.join(__dirname, "./src")]
		}
	},
	webpackMiddleware: {
		noInfo: true
	},
	plugins: [
		require("karma-webpack"),
		require("karma-jasmine"),
		require("karma-chrome-launcher")
	],
    reporters: ["dots"],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ["Chrome"],
    singleRun: false
  });
};

If you’re familiar with Karma, this config file should look pretty normal; the only things I’ve changed were the additions of the Webpack preprocessor, the Webpack configuration section, and some extra plugins required for Karma to run the Webpack bundling process. First, I’ve told Karma to look through files that end with “.test.js” under my test folder to be used as entry points into the Webpack bundler by specifying as such under the preprocessors section. Next, the Webpack configuration has been added and is a carbon copy of the standard Webpack configuration for when I’m actively working on the app, with the notable exception of the entry point. It’s not necessary, as all of the entry points are specified in the preprocessor section. For a complete overview of what each of the configuration statements in the Webpack section mean, take a look at my Webpack article here. The webpackMiddlewaresection indicates that the webpack-dev-server should hide any logging messages from the Karma console. Finally, the plugins section specifies that Karma should run the karma-webpack, karma-jasmine and karma-chrome-launcher plugins.

Note: Typically, Karma will pull in the formatter automatically, but for some reason, I had to add it in specifically due to the inclusion of the karma-webpack plugin by adding require("karma-jasmine") after the karma-webpack plugin declaration.

What does a test look like?

Great question.

var Gravatar = require("components/gravatar"),
	
	React = require("react"),
	TestUtils = React.addons.TestUtils,
	
	md5 = require("blueimp-md5");

describe("components - gravatar - ", function() {
	it("should set image source with Gravatar url and hashed email address", function() {
		var email = "test@email.com",
			gravatar = TestUtils.renderIntoDocument(),
			dom = TestUtils.findRenderedDOMComponentWithTag(gravatar, "div").getDOMNode();
		
		expect(dom.querySelector("img").getAttribute("src") === "http://www.gravatar.com/avatar/" + md5(email)).toBe(true);
	});
});
"use strict";

var React = require("react"),
	md5 = require("blueimp-md5");

require("./style.less");

module.exports = React.createClass({
	render: function() {
		var url = "http://www.gravatar.com/avatar/" + md5(this.props.email);
		if (this.props.size)
			url += "?s=" + this.props.size;
		return <div className="gravatar">
			<img src={url} />
		</div>;
	}
});

If you’re unfamiliar with how Gravatar works, here’s a quick overview. The service allows the caller to provide an MD5-hashed email address as a unique identifier, and then returns an image that user has specified, or a silhouette if no such user exists. There are some other options you can pass, like size, for example, which I’m adding above if a size is passed in via props.

In our test above, we’re making use of React’s TestUtils object to provide us with some utilities made specifically for unit testing. First, we create a new instance of the Gravatar class, then render it using the renderIntoDocument test call. Then, we retrieve the rendered DOM element and check the src attribute of the image and confirm it’s pointing toward Gravatar. If all is well, running karma start will result in a success message. Hooray!

Conclusion

I thought testing with Webpack would be somewhat challenging, especially with the added hurdle of compiling React’s JSX classes into raw JavaScript, but it turned out to be relatively straightforward, with only a few little difficulties along the way. Thanks for reading!

gisonline-me-gray