Grunt: Automated Testing

With a look into our evolving JavaScript testing methods, Patrick shares how we're using Mocha, Jasmine, and Grunt.

Introduction

A few months ago we posted about our new build process, and we mentioned starting to use Grunt for the “freedom and customization” that it provided. In addition, we noted that we're using the Jasmine plugin, which allows for JavaScript testing.

Recently, Rob Tarr released a new application called Stuntman. This application (using node.js) had the need for both client and server-side JavaScript testing. Our new build process already had Jasmine running our client JavaScript specs, but we also had to test our server-side JavaScript.

Grunt to the Rescue

Running both server and client-side JavaScript specs was an issue. We had not yet seen a good implementation of Jasmine running both server and client specs, nor did we find a Mocha plugin that ran server and client specs.

Our solution:

  • Test client side JavaScript with Jasmine
  • Test our server side JavaScript with Mocha

Not very challenging to do thanks to Grunt, but there are some important packages you will need.

Prerequisites

  • Node.js
  • npm

Grunt Setup

Check out the example repository.

Our basic setup consists of the following packages in our package.json file:

Example “package.json”

{
"name": "MochaJasmine",
"description": "Mocha and Jasmine Testing",
"version": "0.0.1",
"devDependencies": {
"grunt": "0.4.x",
"grunt-contrib-watch": "~0.2.0",
"grunt-contrib-jshint": "~0.4.3",
"grunt-contrib-jasmine": "~0.4.2",
"grunt-mocha-cli": "~1.0.2",
"phantomjs": "1.8.2-0",
"expect.js": "~0.2.0"
}
}
view raw package.json hosted with ❤ by GitHub

Gruntfile.js Setup

module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON("package.json"),
watch: {
grunt: {
files: ["Gruntfile.js", "package.json"],
tasks: "default"
},
javascript: {
files: ["src/client/**/*.js", "src/server/*.js", "specs/**/*Spec.js"],
tasks: "test"
}
},
jasmine: {
src: "src/client/js/*.js",
options: {
specs: "specs/client/*Spec.js"
}
},
mochacli: {
options: {
reporter: "nyan",
ui: "tdd"
},
all: ["specs/server/*Spec.js"]
},
jshint: {
all: [
"Gruntfile.js",
"src/**/*.js",
"spec/**/*.js"
],
options: {
jshintrc: ".jshintrc"
}
}
});
grunt.loadNpmTasks("grunt-contrib-watch");
grunt.loadNpmTasks("grunt-contrib-jshint");
grunt.loadNpmTasks("grunt-contrib-jasmine");
grunt.loadNpmTasks("grunt-mocha-cli");
grunt.registerTask("test", ["jshint", "mochacli", "jasmine"]);
grunt.registerTask("default", ["test"]);
};
view raw Gruntfile.js hosted with ❤ by GitHub

Once you have your package.json and Gruntfile.js in a directory, install the packages with the following command (in your project directory):

npm install

Directory Structure

The specs are contained within the specs directory, following the server/client model:

  • /specs/client
  • /specs/server

The source files are located in the following directories:

  • /src/client/js
  • /src/server

Example Tests

Client 

var AppClient = function() {
this.name = "Panda";
};
AppClient.prototype.setName = function(name) {
this.name = name;
};
AppClient.prototype.getName = function() {
return this.name;
};
view raw client.js hosted with ❤ by GitHub
describe("The Client App...", function() {
var client;
beforeEach(function(){
client = new AppClient();
});
it("is a constructable object", function() {
expect(typeof client).not.toBeUndefined();
});
});
view raw clientSpec.js hosted with ❤ by GitHub

Server 

var App;
function App() {
this.panda = "sad";
}
module.exports = App;
view raw app.js hosted with ❤ by GitHub
var App, app, expect;
App = require("../../src/server/server.js"); //Server side code
expect = require("expect.js");
app = new App();
describe("server testing", function() {
it("should work", function() {
expect(typeof app).to.be("object");
});
});
view raw appSpec.js hosted with ❤ by GitHub

Running Tests

Now we have Jasmine running our client-side specs, and we have Mocha (CLI) running our server-side specs. You can use Grunt watch to track your files for changes and re-run the tasks specified in the Gruntfile.js. The results of the tests will be displayed in terminal, and you could easily add notify too.

Grunt will watch your files for changes and rerun the tests with Watch.

To have Grunt watch your files for changes and rerun tests, in the project directory run the command: 

grunt watch

Or you can run tests right away with the following:

grunt test

or

grunt

Grunt test screenshot

Trial and Error

We tried to use only Mocha, but we ran into issues with running client-side tests. One issue for us was that Mocha expects client-side JavaScript to be in the 'require' format, which would cause a refactor for all client-side code just to get tests to run. We also tried a Jasmine-only approach; however, we encountered problems getting Jasmine to handle both client and server-side specs.

In the end, Mocha was good at server-side tests while Jasmine was good at client-side tests, so we thought, "why not use both?" Though using different testing frameworks felt a little strange to us at first, Grunt ran each test suite automatically (rather than running separate commands for each framework). It didn't cause us issues.

This is where we are right now with JavaScript testing. But like anything else that's new, it will probably change—especially with us at Sparkbox. We're always looking for ways to improve our process, and we encourage you to do the same!