Browser Testing with PhantomJS and Mocha – Part 2

Happy New Year!

Today I am going to complete the work of browser testing. In the last article, I introduced MochaJS in a browser, so you could run tests manually in a browser by setting up a test server and generating a static site. I am going to automate that task and take the browser out of the mix.

A big part of the process is the inclusion of PhantomJS – a headless browser that allows you to do a number of things – among them is headless browser testing. There are plug-ins for most test runners, including Mocha, Jasmine, and Chutzpah.

Before I get to that, I need a strategy. My build process is driven by gulp. I run gulp test to build the library and run all the tests. I need a task that will set up a test web server, then use phantomJS and mocha to run the test suite, bailing on a failed test, and then finally shutting down the test web server. I’ve already discussed the test server, but that runs forever.

Fortunately for me, Mocha and PhantomJS is such a popular combination that there is a Gulp plug-in for the combo called gulp-mocha-phantomjs, which is really a thin wrapper around mocha-phantomjs. PhantomJS is bundled here, so it should “just work”. I did have some trouble getting PhantomJS working on Mac OSX El Capitan due to the security policies. To fix this, open System Preferences, then Security & Privacy. There is a section to Allow applications downloaded from Anywhere:


The Gulp task looks like this:

var gulp = require('gulp'),
    express = require('express'),
    phantomjs = require('gulp-mocha-phantomjs'),
    runSequence = require('run-sequence'),
    config = require('../configuration');

var port = config.test.server.port || 3000;
var server = 'http://localhost:' + port + '/';
var listening;

var app = express();

gulp.task('browser:global', function () {
    var stream = phantomjs({ reporter: 'spec' });
    stream.write({ path: server + 'global.html' });
    return stream;

gulp.task('testserver:close', function (callback) {
    console.log('Test Server stopped');

module.exports = exports = function (callback) {
    listening = app.listen(port, function () {
        console.log('Test Server started on port ', port);
        runSequence('browser:global', 'testserver:close', callback);

The task uses a global variable, listening, to store the server reference. This is used within the testserver:close task to close the connection and make the server quit. The main task sets up the server to listen. When the server is listening, it runs the test suites in order. I’ve only got one test suite right now. If I were expanding this to other test suites, I would add a task for each test suite and then add the task to the runSequence call before the testserve:close task.

I’ve linked the task into my main Gulpfile.js like this:

var gulp = require('gulp');

gulp.task('lint', require('./gulp/tasks/lint'));
gulp.task('mocha', require('./gulp/tasks/mocha'));

gulp.task('build:testserver', [ 'build' ], require('./gulp/tasks/buildserver'));
gulp.task('browser-tests', [ 'build:testserver' ], require('./gulp/tasks/browsertests'));

gulp.task('build', [ 'lint', 'mocha' ], require('./gulp/tasks/build'));
gulp.task('test', ['lint', 'mocha', 'browser-tests']);
gulp.task('default', ['build', 'test']);

The task is stored in gulp/tasks/browsertests.js. This sequencing ensures that the main test suite and linter is done first, then I build the library and then I run the browser tests. Output should now look like this:


There is a small problem – the server continues to run (and the process never exits) if the browser tests fail. However, I find that reasonable since I will want to load the failing test up into a web browser to investigate if the tests fail.