Choose a random port when testing on your CI-server

Published: 2013-12-30 by Lars  pipeline

When running our automated tests, we sometimes need a running web server. A back-end example is when we write (integration) tests against our web service end-points. A front-end example is when we use the text-plugin for RequireJS to load HTML templates (https://github.com/requirejs/text#xhr-restrictions).

We can have multiple build jobs running on our CI-server, which is useful wen we have a long build pipe-line or run builds for several release branches in addition to running builds for master / trunk. Being able to run multiple build jobs allows us to run jobs in parallel, potentially cutting down drastically on the feedback loop for new commits.

However, to avoid problems when running multiple build jobs in parallel, we must take care that each build job uses its own resources. So build jobs need to be configured to use their own database, for example, and also to use their own web server port. In this blog post I will show how to run JavaScript unit tests against a web-server using a random available port and thereby supporting parallel build jobs.

In this example I will use GruntJS and QUnit. Similar things can be done for other testing frameworks, like Jasmine.

To run QUnit tests against a web server with GruntJS, we would normally configure a 'test' task to first spin up a server (using port 8082 in this example) and then run tests against that port:

gruntConfig.qunit = {
  serve: { 
    options: { 
      urls: ['http://localhost:8082/test/index.html']
    }
  }
};
gruntConfig.connect = {
  test: {
    options: {
      port: 8082,
      base: 'src'
    }
  }
};
grunt.registerTask('test', ['connect:test', 'qunit:serve']);

However, this fixes the port number. The grunt-contrib-connect plugin has a feature where we can ask it to choose any random available port, and then we need to somehow put that information into the URL used by grunt-contrib-qunit. Here is one way to do it, which relies on a convenient event containing the port number as an argument, which is emitted when the connection has been made:

gruntConfig.qunit = {
  serve: { 
    options: { 
      urls: [/* specified in test-on-random-port */]
    }
  }
};
gruntConfig.connect = {
  test: {
    options: {
      port: 0, // Note: configure connect to choose a randomly available port
      base: 'src'
    }
  }
};
grunt.registerTask('test-on-random-port', function () {
  grunt.event.once('connect.test.listening', function (host, actualPort) {
    var url = 'http://localhost:' + actualPort + '/test/index.html';
    gruntConfig.qunit.serve.options.urls = [url]; // Note: configure qunit to connect to the chosen port
    grunt.task.run('qunit:serve');
  });
  grunt.task.run('connect:test');
});
grunt.registerTask('test', ['test-on-random-port']);

A working sample project can be found at https://github.com/larsthorup/qunit-demo-advanced.

Discuss on Twitter