Build your own TDD boilerplate project for JavaScript

Getting started with tests takes time because there are many small decisions to make. If you haven't gone through the whole setup and evolution more than once or twice, it's easy to get lost or just plain forget.

This post aims to walk through the creation of a TDD boilerplate project for re-use when driving your JavaScript tests. We'll set up the project to work with GitHub, node.js, mocha, rawgit, testem and travis. All steps were re-learned the hard way while I worked through my own where.js project in January-February 2014.

Special thanks to these wonderful people for their patient attention to detail and their marvelous feedback

Update: 25 NOV 2014 ~ links to rawgithub have been modified to point to the rawgit.com domain.

Cross-platform emphasis

The JavaScript you'll see here is designed (hacked) to run on both node.js and in the browser without using a special loader or packager like browserify or require.js. The point is that we first get the project started and get comfortable with the TDD workflow in both runtimes.

How long?

60-90 minutes, depending on what you have already set up, and how many of the suggested breaks you take.

tl;dr

Here's my own version of the project we're going to build ~ dfkaye/tdd-boilerplate

Workflow

This sequence seems long, as there are several decisions to make. We'll do just enough so you can see the process itself take shape. The most important sections are those on mocha and testem. However, the sequence is done in an order that shakes out the structural kinks in the workflow.

Between each section, I recommend taking a break before moving on.

set up GitHub

We'll start by setting up the project on your system, then on GitHub. While these steps can be done separately, I recommend doing them together - right away - so you can verify that committing your changes actually works.

set up node.js

After GitHub, we'll set up node.js, a server-side runtime for JavaScript, to drive our tests and development.

add mocha tests

We'll use npm, included with node.js, to install mocha, a very flexible JavaScript test runner by TJ Holowaychuk, and start driving some tests.

add npm test

We'll add a way to drive the suite through node.js without specifying node in the command each time.

add browser suite

We'll create a browser suite for mocha that we can view as a standalone or static page.

We'll look at using rawgit, a proxy service originally set up by Ryan Grove, for loading static pages from your GitHub repo, so you can view the running browser suite online.

add testem

We'll use npm to install testem, a node.js socket-based test harness developed by Toby Ho, for both node and cross-browser development to drive all our suites simultaneously.

add travis hook

We'll add support for travis, a remote service that runs your projects tests each time you commit to GitHub.

next steps

Short list of other test libraries and projects.

----------------------- GET STARTED ------------------------

GitHub repo

If you've not used it before, GitHub is an online repository for storing and sharing your projects using git, the distributed version control system. To use them you will need to install git or msysgit from GitHub

(I leave the setup details as an exercise for the reader.)

Once you're set up with git and GitHub, open your command line shell or git-bash terminal.

Create the local project directory with git files

We need a place to start locally so let's create the directory and initialize it for use with git and GitHub.

On the command line create and initialize the project folder, e.g.,

mkdir tdd-boilerplate
cd tdd-boilerplate
git init

You should see a message like Initialized empty Git repository in [path/to]/tdd-boilerplate/.git/

Create the remote project on GitHub

GitHub makes setting a up new project pretty easy. We'll use the online service to do that.

Open a browser and create the new project on GitHub

  • name it 'tdd-boilerplate'
  • add a short description (you can change this later)
  • choose public, not private (show your work)
  • select initialize with a README
  • add .gitignore file - choose Node
  • add a license - choose MIT (you can change this later)
  • click Create Repository

You should be redirected your new tdd-boilerplate project page.

Note The .gitignore file should have a handful open of entries - these are files or directories that git will ignore whenever your add, commit, and push changes. In this case, by choosing Node, we'll have entry for node_modules so that any npm modules we install won't be pushed to GitHub.

Sync up with GitHub

We need the remote files we just created to be sync'd with our local directory. Git will detect the changes that we're about to make to our local files and can then commit those to the remote repository at GitHub.

First we need to tell git about our remote repo and assign it as origin. Enter the following at the command line, replacing YOUR_NAME with your GitHub username:

git remote add origin git@github.com:YOUR_NAME/tdd-boilerplate.git

Now pull down the license, README and .gitignore files from GitHub

git pull origin master

You should see a message similar to this

remote: Counting objects: 5, done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 5 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (5/5), done.
From github.com:YOUR_NAME/tdd-boilerplate
 * branch            master     -> FETCH_HEAD

Let's add a file to the project with nothing in it

touch index.js

We'll fill this in eventually with actual JavaScript. Right now we just want to verify we can commit successfully to the remote repository.

Commit our changes to git, and push them to GitHub.

git status
git add .
git commit -am 'first commit; sync up'
git push -u origin master

You should see a success message ending with something like

025cf88..103f972  master -> master
Branch master set up to track remote branch master from origin.

Verify this check-in

Open the repo on github.com (https://github.com/YOUR_NAME/tdd-boilerplate), and verify the index.js file is listed.

Now we're in sync.

addendum: .git/config

Working on this project and receiving enlightening feedback from reviewers, I added this short bit to explain the .git/config file's contents.

When we initialized the project with git init, git added several directories and files for itself to track our changes and so forth.

Open the file at /tdd-boilerplate/.git/config. Depending on your git client settings, the first section should resemble

[core]
  repositoryformatversion = 0
  filemode = false
  bare = false
  logallrefupdates = true
  symlinks = false
  ignorecase = true
  hideDotFiles = dotGitOnly    

Verify these lines appear

[remote "origin"]
  url = git@github.com:YOUR_NAME/tdd-boilerplate.git
  fetch = +refs/heads/*:refs/remotes/origin/*

This was added by git when we used the git remote add origin command.

Verify these lines appear

[branch "master"]
  remote = origin
  merge = refs/heads/master

This was added by git when we used the git push -u origin master command.

While you can modify this file directly if you wish, be sure to consult GitHub documentation about Adding a Remote for more information.

----------------------- BREAK TIME ------------------------

node.js

If you haven't used it, go get node.js, a server-side runtime for JavaScript.

(I leave the setup details as an exercise for the reader.)

Once you're set up with node.js, open your command line shell or git-bash terminal.

Configure the npm package

Every node.js module or project should have a package descriptor file, named package.json, which makes it re-usable by other developers as well as services such as travis which we'll see later.

Use npm init to create the package.json file at the root of the project. This utility prompts you at the command line for a title, description, and some other things. Just get through enough steps to create the file.

cd tdd-boilerplate
npm init

npm init will output the package.json file text before saving. You should see something like this

{
  "name": "tdd-boilerplate",
  "version": "0.0.0",
  "description": "TDD boilerplate for JavaScript projects",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "git://github.com/YOUR_NAME/tdd-boilerplate.git"
  },
  "keywords": [
    "tdd",
    "javascript",
    "boilerplate",
    "testem",
    "rawgithub",
    "rawgit"
  ],
  "author": "YOUR_NAME <YOUR@EMAILADDRESS.com>",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/YOUR_NAME/tdd-boilerplate/issues"
  }
}


Is this ok? (yes) 

Type yes to save.

Commit our changes to git, and push them to GitHub.

git status
git add .
git commit -am 'add package.json'
git push

----------------------- BREAK TIME ------------------------

mocha suite

We'll use npm to install mocha, the JavaScript test runner. Then we'll create an actual test and verify that we can run it from the command line.

Install mocha

At the command line:

cd tdd-boilerplate
npm install mocha --save-dev

This will install mocha to a node_modules directory in the project, and add mocha as a devDependency to the package.json file.

Note The node_modules directory should be listed in the .gitignore file we created when getting started with GitHub. That entry prevents the node_modules directory from being checked in and pushed to GitHub.

Create test directory for mocha and some placeholder test files

We'll create a test that can be run from either node or the browser. We'll then create a node-specific suite to drive the mocha/suite.

mkdir test test/mocha
touch test/mocha/suite.js
touch test/mocha/node-suite.js

Create a test

Open test/mocha/suite.js and add the following boilerplate:

// mocha/suite.js

var boilerplate;

if (typeof require == 'function') {
  // enable to re-use in a browser without require.js
  boilerplate = 'boilerplate';
}

describe('smoke test', function() {
  it('should pass', function () {
    assert.equal(boilerplate, 'boilerplate', 'failure message');
  });
});

Create the node-suite

We'll create the node-specific suite for our test that drives mocha programmatically. That will allow us to use npm test command further down which will give us a way to add specific test commands to our package.json file.

Open test/mocha/node-suite.js and add the following to load the mocha tests:

// test/mocha/node-suite.js

// This runs with mocha programmatically rather than from the command line.
// how-to-with-comments taken from https://github.com/itaylor/qunit-mocha-ui

//Load mocha
var Mocha = require("mocha");

//Tell mocha to use the interface.
var mocha = new Mocha({ui:"bdd", reporter:"spec"});

//Add your test files
mocha.addFile("./test/mocha/suite.js");

//Run your tests
mocha.run(function(failures){
  process.exit(failures);
});

Verify it from node.js

Tell node.js to run the node-suite by the following command

node ./test/mocha/node-suite.js

You should see an error message

$ node ./test/mocha/node-suite.js

  .

  0 passing (4ms)
  1 failing

  1) smoke test should pass:
     ReferenceError: assert is not defined    

Aha! We need to require the assert module that ships with node.js first

// mocha/suite.js

var boilerplate;

// ... ADD THIS LINE ...
var assert;

if (typeof require == 'function') {
  // enable to re-use in a browser without require.js
  boilerplate = 'boilerplate';

  // ... ADD THIS LINE ...
  assert =  require('assert');
}

describe('smoke test', function() {
  it('should pass', function () {
    assert.equal(boilerplate, 'boilerplate', 'failure message');
  });
});

Re-run the test

node ./test/mocha/node-suite.js

That should pass

$ node ./test/mocha/node-suite.js
  .

  1 passing (4ms)

Create the code to be tested

touch index.js

Open the index.js file and add the following IIFE

// main boilerplate

;(function () {

  // node.js
  if (typeof module != "undefined") {
    module.exports = boilerplate;
  }
  // browser
  if (typeof window != "undefined") {
    !window.boilerplate && (window.boilerplate = boilerplate);
  }

  function boilerplate() {
    return 'boilerplate';
  }
}());

Note The node-vs-browser assignment blocks are useful for getting started with tests that can run on either environment. You can replace that later with a loading library of your choice.

Open the mocha/suite.js file and change our boilerplate assignment to load
index.js:

if (typeof require == 'function') {
  // enable to re-use in a browser without require.js

  // ... COMMENT OR REMOVE THIS LINE...
  //boilerplate = 'boilerplate';

  // ... ADD THIS LINE ...
  boilerplate = require('../../index.js');

  assert = require('assert');
}

Re-run the node suite

node ./test/mocha/node-suite.js

You should see an error message

$ node ./test/mocha/node-suite.js
  .

  0 passing (4ms)
  1 failing

  1) smoke test should pass:
     AssertionError: failure message

Aha! we need to modify the reference to boilerplate() as a method call in our assert statement.

describe('smoke test', function() {
  it('should pass', function () {

    // ... COMMENT OR REMOVE THIS LINE...  
    //assert.equal(boilerplate, 'boilerplate', 'failure message');

    // ... ADD THIS LINE ...         
    assert.equal(boilerplate(), 'boilerplate', 'failure message');
  });
});

Re-run the test

node ./test/mocha/node-suite.js

That should pass.

Commit our changes to git, and push them to GitHub.

git status
git add .
git commit -am 'first mocha test and node suite'
git push

----------------------- BREAK TIME ------------------------

npm test

The npm test command allows us to run specific tests in our project without having to specify node in the command line. That lets other programs and services exercise our tests more easily.

npm scripts entry

Add an entry in package.json as

"scripts" : {
  "test": "node test/mocha/node-suite.js"
},

including the trailing comma if it's not the last entry. The test entry should be present with an echo statement if you followed the npm init step above.

Now try the test command to run the mocha tests on node.js

npm test

Should see mocha's spec reporter listing the single passing smoke test.

...
smoke test
  v should pass


1 passing (8ms)

Commit our changes to git, and push them to GitHub.

git status
git add .
git commit -am 'npm test script'
git push

----------------------- BREAK TIME ------------------------

browser suite

In this section we'll create an HTML file that runs our tests with mocha, and which we can view as a static page. We'll modify it later to work with testem.

To run mocha in the browser you'll need mocha.js and mocha.css files that are separate from mocha's node.js bundle.

Add mocha.css and mocha.js vendor files

Create a /vendor/mocha directory

cd tdd-boilerplate
mkdir vendor vendor/mocha

Fetch these files and save them in the vendor/mocha directory.

If you have curl you can do these

curl https://raw.githubusercontent.com/visionmedia/mocha/master/mocha.css > ./vendor/mocha/mocha.css
curl https://raw.githubusercontent.com/visionmedia/mocha/master/mocha.js > ./vendor/mocha/mocha.js

else you can navigate to the files directly in a browser and copy+paste their content to the respective target file:

Be Advised When copying the file text into your editor, be sure FIRST to set the file's encoding to UTF-8. ASCII encoding will force conversions of entities, such as the checkmarks, to question marks :(

Create an HTML file for our browser suite

touch test/mocha/browser-suite.html

Open the test/mocha/browser-suite.html file and add the following

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>tdd-boilerplate mocha browser-suite</title>
    <link rel="stylesheet" href="../../vendor/mocha/mocha.css" />
  </head>
  <body>
    <div id="mocha"></div>
    <script src="../../index.js"></script>

    <script src="../../vendor/mocha/mocha.js"></script>
    <script>mocha.setup('bdd')</script>

    <!-- get tests and start -->
    <script src="./suite.js"></script>
    <script>
      mocha.checkLeaks();
      mocha.globals(['boilerplate']); // watch our boilerplate function
      mocha.run();
    </script>        
  </body>
</html>

Run the browser tests

Open this file in your browser to verify file paths are correct (mocha runs, the styles are correct).

Hold it! You should see a failing test as there is no assert module in the browser.

Add assert.js

Fetch the browser version of assert and place it in the /vendor directory directory

curl https://raw.githubusercontent.com/Jxck/assert/master/assert.js -o ./vendor/assert.js

or copy https://raw.githubusercontent.com/Jxck/assert/master/assert.js to /vendor/assert.js

Reminder When copying the file text into your editor, be sure FIRST to set the file's encoding to UTF-8. ASCII encoding will force conversions of entities, such as the checkmarks, to question marks :(

Open browser-suite.html and add this script tag just before mocha/mocha.js

<script src="../../vendor/assert.js"></script>

Re-load the browser-suite in your browser to verify that the single test passes.

Commit our changes to git, and push them to GitHub.

git status
git add .
git commit -am 'browser suite; mocha files; assert; '
git push

----------------------- BREAK TIME ------------------------

rawgit

This optional step in the process is very short but worth considering for a moment.

I like using rawgit (formerly rawgithub) to confirm that the project's browser tests really are accessing the correct file paths remotely, and to share these tests with others online.

Open the README page and add the following markdown

# rawgit

+ [mocha suite](https://rawgit.com/YOUR_NAME/YOUR_PROJECT/master/test/mocha/browser-suite.html)

Commit our changes to git, and push them to GitHub.

git status
git add .
git commit -am 'rawgit'
git push

View the browser suites on rawgit

Give it a moment, then view the project page on GitHub and visit the rawgit page via the new link.

----------------------- BREAK TIME ------------------------

testem

The testem harness makes cross-browser TDD extremely fast. In this section we'll add testem to our workflow and use it to run all tests on node and in multiple browsers.

Install testem

testem is a npm module so we can install it easily

npm install testem -g

Use the -g flag to install testem globally, so you can re-use it easily on other projects.

Refactor the browser suite for use with testem

Open test/mocha/browser-suite.html and add the following inline script block (between mocha.setup and start comment) ...

<script>
  if (location.href.indexOf('://localhost:7357') != -1) {
    // testem goes between test runner setup and the tests
    document.write('<script src="/testem.js">' + '<\/' + 'script>');
  }
</script>

<!-- get tests and start -->
...

Try it with testem

Tell testem to run, forwarding the browser to the URL of our test page

testem -t ./test/mocha/browser-suite.html

You should see testem open in the shell waiting for tests.

Open a browser manually

You can open any browser manually and go to http://localhost:7357

The browser should forward to http://localhost:7357/test/mocha/browser-suite.html and you should now see the browser suite with results, and see that testem has printed the results in the shell.

Hit q to stop testem.

Open a browser with a testem launcher

You can use a testem launcher for the particular browser you want to try. To open the test automatically in chrome, start testem by running the following:

testem -l chrome

Chrome should open and forward to http://localhost:7357/test/mocha/browser-suite.html and you should now see the browser suite with results, and see that testem has printed the results in the shell.

Hit q to stop testem.

Configure testem to launch the node.js suite with a custom launcher

We'll define a custom launcher for testem that runs our node suite. To do that we need a configuration file for testem, called testem.json.

Create the testem.json file

touch testem.json

Add the following

{
  "launchers": {
    "mocha" : {
      "command": "node ./test/mocha/node-suite.js"
    }
  }
}

Verify this works by starting testem from the command line

testem -l mocha

You should see the node test passing in the shell. If your browser is still open you should also see the browser suite passing in the shell.

Hit q to stop testem.

Configure npm scripts to run testem in the browser

Now we'd like to run both node.js and browser-suite in testem at the same time. To set that up, add the testem entry in the scripts section of your project's package.json file:

"scripts" : {
  "test": "node ./test/mocha/node-suite.js",
  "testem": "testem -l mocha -t ./test/mocha/browser-suite.html" 
}

Note that the testem entry contains both the mocha launcher and the -t flag for the browser suite.

Start both node and browser suites with testem from the command line

npm run testem

You should see the passing test in both node and browser.

Commit our changes to git, and push them to GitHub.

git status
git add .
git commit -am 'testem, scripts, launcher commands'
git push

----------------------- BREAK TIME ------------------------

travis

Once you have several tests in place and have made several commits to GitHub, it's nice to have a service that runs tests for you on check-in. Travis is just such a service, allowing you continuous verification. This is especially important when you are working with others who make regular updates to the project.

Set the project to notify travis

Visit the travis profile page to set up travis to be notified by GitHub every time you commit something to your repo. You will be prompted to sign in with your GitHub credentials if you haven't done this before.

On the travis profile, visit the Accounts page (mouseover your gravitar at the upper right - the Accounts link will appear).

Find the tdd-boilerplate repo in the list that appears. If it's not there, click Sync Now to refresh travis from GitHub. When you find the repo listed, click the Off button to the On state.

Now travis will be able to run the npm test command on your repo every time you push changes to GitHub.

Verify the service hook for travis on GitHub

  • go to the service hooks page for the project at https://github.com/YOUR_NAME/tdd-boilerplate/settings/hooks
  • select Configure Services
  • should find travis in the list that appears - it should be checked

Configure travis for your project

Create a .travis.yml file (leading dot included)

touch .travis.yml

Enter the following for travis to run the node.js tests:

language: node_js
node_js:
  - "0.11"
  - "0.10"
  - "0.08"

You don't need multiple node.js versions. I add more than one in case of API changes I've missed.

Add a travis build status badge

This is nice to have on your project's README as it's not only an image but a link to the travis test result.

Here's the build status link/badge for my version of tdd-boilerplate:

Build Status

Copy, paste and modify the following at the top of your version of the README

[![Build Status](https://travis-ci.org/YOUR_NAME/tdd-boilerplate.png?branch=master)](https://travis-ci.org/YOUR_NAME/tdd-boilerplate)

Commit our changes to git, and push them to GitHub.

git status
git add .
git commit -am 'travis hook'
git push

Travis should notify you by email whether the build passed.

You can also go to the project page on GitHub and visit the travis profile page via the travis badge image. There you can see the test run for the last commit.

----------------------- CELEBRATE ------------------------

Voilà!

You now have a TDD boilerplate to get started testing JavaScript projects, complete with travis, npm test commands, testem launchers, and re-usable browser suite viewable as standalone, or on rawgit, or with testem.

You can start moving things around to your liking, clean up the relative paths in the browser suite, for example, or move the node_modules directory out of the project (I usually put it in a parallel directory to all my projects), or go with your preferred loader solution if you're doing things more for the browser than for node.js. And so on…

Next