The Headless Horseman Pursuing Ichabod Crane — John Quidor

Replace PhantomJS with Headless Chromium for JavaScript Unit Testing in Karma

Last year the primary maintainer of PhantomJS announced he would no longer be contributing to the project.

PhantomJS is a “headless” internet browser, meaning it has no user interface. On top of that, PhantomJS can be installed on the same machine as your application by adding phantomjs-prebuilt as an npm dependency, which works with Windows, macOS, and Linux. Both of these attributes make it a popular choice for continuous integration testing, as build servers often don’t support graphical interfaces, and developers may not have the ability or desire to install programs on the machines prior to running tests.

While it appears another developer has stepped up to continue the project, if you’re using PhantomJS for testing it’s probably in your best interests to switch. PhantomJS is lagging well behind other browsers to implement support for ECMAScript 6 features, and according to the retired maintainer in his departure annoucement, PhantomJS “eat[s] memory like crazy.” In fact, he specifically mentions Chrome as the superior alternative:

“Chrome is faster and more stable than PhantomJS.”

Fortunately, I discovered that switching to Chrome was easy. Err… switching to Chromium was easy.

Chrome vs Chromium

Chromium is a fully-functional open source internet browser created by Google which can be used on its own or modified and expanded to create other browsers. Google Chrome, Opera, and Vivaldi are all Chromium-based browsers. Chromium handles the “meat and potatoes” of internet browsing, like navigating to and loading webpages.

The important thing here is that Chromium is maintained by Google and used as the core of Google Chrome. This means we can be confident that JavaScript code executed by Chromium will behave the same in Chrome, and Chrome is currently the most popular internet browser by a lot. Just like PhantomJS, we can install Chromium through npm on any OS thanks to the puppeteer package. So Chromium is a great choice for JavaScript unit testing.

Migrating Karma from PhantomJS to Headless Chromium

The rest of this article is specific to projects using Karma. Karma is a test runner for JavaScript which will execute your tests in a real browser (or browsers!) and display output in your command line. It’s great!

If you’re currently testing with PhantomJS, you’ll want to uninstall a couple dependencies you’ll no longer be using:

npm r phantomjs-prebuilt karma-phantomjs-launcher

(npm r is short for npm remove, which is an alias for npm uninstall. Think of all the keystrokes you’ll save!)

Now you can install karma-chrome-launcher and puppeteer, the latter of which will download and install Chromium on your machine:

npm i -D karma-chrome-launcher puppeteer

(npm i is short for npm install ya dingus. The -D flag is a shortcut for --save-dev which means the packages will be installed under “devDependencies.”)

Finally, you’ll just need to make a couple changes to your Karma configuration file, typically “karma.conf.js.” I’ve only included the relevant portions of the file:

const puppeteer = require('puppeteer');
process.env.CHROME_BIN = puppeteer.executablePath();
module.exports = function(config) {
// omitted...
browsers: ['ChromeHeadless']
// omitted...

The top two lines are used to tell karma-chrome-launcher where Chromium was downloaded and installed to by puppeteer. The browsers setting is used to instruct karma-chrome-launcher to launch Chromium in headless mode. Yes, I know it says CHROME_BIN and ChromeHeadless instead of CHROMIUM_BIN and ChromiumHeadless, but I won’t tell anyone if you don’t.

Amazingly, that’s it. If you run karma start you should see see normal Karma output and watch your tests get executed by Chromium. When the code is copied to another machine and npm install is executed, puppeteer will magically download and install Chromium. Then you’ll be all set to run your tests with Karma.

For more advanced usage of Chromium with Karma (for example, if you need to pass some options to Chromium when it launches), see “Automated testing with Headless Chrome” over on Google’s Web Fundamentals site.