Fretless

Debugging Karma + PhantomJS
By Davey Strus /

tl;dr: Add the lines highlighted below to karma.conf.js and open http://localhost:9876/debug.html in Chrome (with dev tools open) while your tests are running:


module.exports = function (config) {
    config.set({
        basePath: '',
        frameworks: ['jasmine', 'angular-cli'],
        plugins: [
            require('karma-jasmine'),
            require('karma-phantomjs-launcher'),
            require('angular-cli/plugins/karma')
        ],
        port: 9876,
        browsers: ['PhantomJS'],
        singleRun: false,
        // Allow remote debugging when using PhantomJS
        customLaunchers: {
            'PhantomJS_custom': {
                base: 'PhantomJS',
                debug: true,
            },
        },
    });
};

As a Ruby developer, I’ve been spoiled by Pry. Among many other things, it lets me add a breakpoint anywhere in my Ruby code—even in tests!—making it darn-near indispensable for debugging.

JavaScript has had the debugger keyword since the beginning, and all the major desktop browsers support it with decent developer tools. We also have console.log. Although it’s nonstandard, and specific implementations vary from browser to browser, you can count on basic console.log support in every major desktop browser.

That would be swell if we still ran JavaScript exclusively in desktop browsers.

I’ve been spending a lot of time with Ionic lately. Although you can target desktop browsers with Ionic, its primary purpose is to build cross-platform mobile apps with JavaScript/TypeScript.

Debugging Ionic doesn’t have to be difficult. Depending on your needs, you may be able to write your Ionic app in such a way that it can still run in a desktop web browser, in which case debugging is no different than in any other JavaScript project. Even if it only runs on iOS and Android, you can still use desktop browser developer tools to debug your code (Safari for debugging iOS apps, Chrome for debugging Android apps).

But what about debugging your test suite?

The Ionic project I’m working on uses Jasmine and Karma for unit tests. Ordinarily, you can debug Karma tests by opening http://localhost:[portNumber]/debug.html in a browser, but we use PhantomJS to run the tests, and the PhantomJS launcher disables this functionality by default. As you may have experienced if you’ve ever used Karma with PhantomJS, any debugger or console.log statements in your code or your tests aren’t going to help you when you run the test suite… unless you use this ONE WEIRD TRICK®!

At the bottom of the README for the PhantomJS launcher plugin for Karma, you’ll find this note:

If you set the debug option to true, you will be instructed to launch a web browser to bring up the debugger. Note that you will want to put debugger; statements in your JavaScript to hit breakpoints. You should be able to put breakpoints in both your test code and your client code.

Let’s look at an abbreviated karma.conf.js file.


module.exports = function (config) {
    config.set({
        basePath: '',
        frameworks: ['jasmine', 'angular-cli'],
        plugins: [
            require('karma-jasmine'),
            require('karma-phantomjs-launcher'),
            require('angular-cli/plugins/karma')
        ],
        port: 9876,
        browsers: ['PhantomJS'],
        singleRun: false,
    });
};

We need to add debug: true somewhere—but where? This isn’t a Karma setting, but rather a setting specific to the PhantomJS launcher. From the example in the README, it looks like we need to do this:


module.exports = function (config) {
    config.set({
        basePath: '',
        frameworks: ['jasmine', 'angular-cli'],
        plugins: [
            require('karma-jasmine'),
            require('karma-phantomjs-launcher'),
            require('angular-cli/plugins/karma')
        ],
        port: 9876,
        browsers: ['PhantomJS'],
        singleRun: false,
        // Allow remote debugging when using PhantomJS
        customLaunchers: {
            'PhantomJS_custom': {
                base: 'PhantomJS',
                debug: true,
            },
        },
    });
};

If all is well, we should be able to use Chrome’s developer tools the next time we run our tests. We’ll need to connect to port 9876, as indicated on line 10 of our Karma configuration file.

Add a debugger; statement somewhere in your code or your tests—just make sure it’s a line that your tests will actually execute—and run your test suite. Open the following URL in Chrome once the tests are running, and make sure you have the developer tools open (⌥⌘I on macOS):


http://localhost:9876/debug.html

With any luck, you should hit your breakpoint. The console is there too, so you can log data just as you would if you were running a normal JavaScript application.

You may notice that the tests keep running in your terminal; they don’t pause. As far as I can tell, the test run that you’re debugging is actually running in Chrome, not PhantomJS, so this may not help you troubleshoot issues that are specific to PhantomJS. It’s really handy for investigating other issues though. It certainly is saving me a lot of trouble!

Please use responsibly, and remember not to commit your code with debugger; statements lying about.

Karma debugging screenshot