How To Use Mocha With Node.js For Test-Driven Development to Avoid Pain and Ship Products Faster

Mocha With Node.js For Test-Driven Development

Test-driven development (TDD) , as many of you might know, is one of the main, agile development techniques. The genius of TDD lies in increased quality of code, faster development resulting from greater programmer confidence, and improved bug detection (duh!).

Historically, web apps have been hard to autotest, and developers relied heavily on manual testing. But, certain parts such as standalone services and REST API can be and should be tested thoroughly by the TDD. At the same time, rich user interface (UI) / user experience (UX) can be tested with headless browsers such as PhantomJS.

The behavior-driven development (BDD) concept is based on TDD. It differs from TDD in language, which encourages collaboration between product owners and programmers.

Similar to building apps themselves, most of the time software engineers should use a testing framework. To get you started with the Node.js testing framework, Mocha, in this post, we cover the following:

  • Installing and understanding Mocha
  • TDD with the assert
  • BDD with Expect.js
  • Project: writing the first BDD test for Blog

The source code for this post is in the ch3 folder of the practicalnode GitHub repository (https://github.com/azat-co/practicalnode).

Installing and Understanding Mocha

Mocha is a mature and powerful testing framework for Node.js. To install it, simply run:

$ npm install –g mocha@1.16.2

Note We use a specific version (the latest as of this writing is 1.16.2) to prevent inconsistency in this book’s examples caused by potential breaking changes in future versions of Mocha.

If you encounter the lack-of-permissions issue when you run the previous command, run this:

$ sudo npm install –g mocha@1.16.2

To avoid using sudo, follow the instructions in from this resource on how to install Node.js correctly.

Tip It’s possible to have a separate version of Mocha for each project by simply pointing to the local version of Mocha, which you install like any other NPM module into node_modules. The command will be:

$ ./node_modules/mocha/bin/mocha test_name

for Mac OS X / Linux.

For an example, refer to “Putting Configs into a Makefile” in the next post on Mocha.

Most of you have heard about TDD and why it’s a good thing to follow. The main idea of TDD is to do the following:

  • Define a unit test
  • Implement the unit
  • Verify that the test passes

BDD is a specialized version of TDD that specifies what needs to be unit-tested from the perspective of business requirements. It’s possible to just write test with good old plain core Node.js module assert. However, as in many other situations, using a framework is more preferable. For both TDD and BDD, we’ll be using the Mocha testing framework because we gain many things for “free.” Among them are the following:

  • Reporting
  • Asynchronous support
  • Rich configurability

Here is a list of optional parameters (options) that the $ mocha [options] command takes:

  • -h or --help: print help information for the Mocha command
  • -V or --version: print the version number that’s being used
  • -r or --require <name>: require a module with the name provided
  • -R or --reporter <name>: use a reporter with the name provided
  • -u or --ui <name>: use the stipulated reporting user interface (such as bdd, tdd)
  • -g or --grep <pattern>: run tests exclusively with a matching pattern
  • -i or --invert: invert the --grep match pattern
  • -t or --timeout <ms>: set the test case time out in milliseconds (for example, 5000)
  • -s or --slow <ms>: set the test threshold in milliseconds (for example, 100)
  • -w or --watch: watch test files for changes while hanging on the terminal
  • -c or --colors: enable colors
  • -C or --no-colors: disable colors
  • -G or --growl: enable Mac OS X Growl notifications
  • -d or --debug: enable the Node.js debugger— $ node --debug
  • --debug-brk: enable the Node.js debugger breaking on the first line— $ node --debug-brk
  • -b or --bail: exit after the first test failure
  • -A or --async-only: set all tests in asynchronous mode
  • --recursive: use tests in subfolders
  • --globals <names>: provide comma-delimited global names
  • --check-leaks: check for leaks in global variables
  • --interfaces: print available interfaces
  • --reporters: print available reporters
  • --compilers <ext>:<module>,...: provide compiler to use

Figure 3–1 shows an example of nyan cat reporter with the command $ mocha test-expect.js -R nyan.

Figure 3–1. Mocha nyan reporter

Figure 3–1. Mocha nyan reporter

Usually, when it comes to choosing a type of framework, there are a few options. Mocha is one of the more robust and widely used. However, the following alternatives to Mocha are worth considering:

Understanding Mocha Hooks

A hook is some logic, typically a function or a few statements, which is executed when the associated event happens.

For example, in my Mongoose course, we use hooks to explore the Mongoose library pre hooks.

Mocha has hooks that are executed in different parts of suites—before the whole suite, before each test, and so on.

In addition to before and beforeEach hooks, there are after(), and afterEach() hooks. They can be used to clean up the testing setup, such as database data

All hooks support asynchronous modes. The same is true for tests as well. For example, the following test suite is synchronous and won’t wait for the response to finish:

describe('homepage', function(){
  it('should respond to GET',function(){
    superagent
      .get('http://localhost:'+port)
      .end(function(res){
        expect(res.status).to.equal(200);
    })
  })

But, as soon as we add a done parameter to the test’s function, our test case waits for the HTTP request to come back:

describe('homepage', function(){
  it('should respond to GET',function(done){
    superagent
      .get('http://localhost:'+port)
      .end(function(res){
        expect(res.status).to.equal(200);
        done();
    })
  })

Test cases (describe) can be nested inside other test cases, and hooks such as before and beforeEach can be mixed in with different test cases on different levels. Nesting of describe constructions is a good idea in large test files.

Sometimes, developers might want to skip a test case/suite (describe.skip() or it.skip()) or make them exclusive (describe.only() or describe.only()). Exclusivity means that only that particular test runs (the opposite of skip).

As an alternative to the BDD interface’s describe, it, before, and others, Mocha supports more traditional TDD interfaces:

  • suite: analogous to describe
  • test: analogous to it
  • setup: analogous to before
  • teardown: analogous to after
  • suiteSetup: analogous to beforeEach
  • suiteTeardown: analogous to afterEach

TDD with the Assert

Let’s write our first tests with the assert library. This library is part of the Node.js core, which makes it easy to access. It has minimal functionality, but it might be enough for some cases, such as unit tests. After global Mocha installation is finished, a test file can be created in a test-example folder:

$ mkdir test-example
$ subl test-example/test.js

Note subl is a Sublime Text alias command. You can use any other editor, such as Vi (vi) or TextMate (mate).

With the following content:

var assert = require('assert');
describe('String#split', function(){
  it('should return an array', function(){
    assert(Array.isArray('a,b,c'.split(',')));
  });
})

We can run this simple test.js(inside the test-example folder), which checks for Array type, with:

$ mocha test
or

$ mocha test.js.

The results of these Mocha commands are shown in Figure 3–2 .

Figure 3–2. Running Array-type test

Figure 3–2. Running Array-type test

We can add to our example another test case (it) that asserts equality of array values:

    var assert = require('assert');
    describe('String#split', function(){
      it('should return an array', function(){
        assert(Array.isArray('a,b,c'.split(',')))
      }); 


    it('should return the same array', function(){
      assert.equal(['a','b','c'].length, a,b,c'.split(',').length, 'arrays have equal length');
    for (var i=0; i<['a','b','c'].length; i++) {
    assert.equal(['a','b','c'][i], 'a,b,c'.split(',')[i], i +'element is equal');
    };
  });
})

As you can see, some code is repeated, so we can abstract it into beforeEach and before constructions:

var assert = require('assert');
var expected, current;
before(function(){
  expected = ['a', 'b', 'c'];
})
describe('String#split', function(){
  beforeEach(function(){
   current = 'a,b,c'.split(',');
  })
  it('should return an array', function(){
    assert(Array.isArray(current));
  });
  it('should return the same array', function({
    assert.equal(expected.length, current.length, 'arrays have equal length');
    for (var i=0; i<expected.length; i++) {
      assert.equal(expected[i], current[i], i + 'element is equal');
    }
  })
})

Chai Assert

In the previous example with test.js and assert, we used the Node.js core module assert. Chai is a subset of that library. We can modify our previous example to use chai assert with following code:

$ npm install chai@1.8.1

And in test-example/test.js:

var assert = require('chai').assert;

The following are some of the methods from the chai assert library:

  • assert(expressions, message): throws an error if the expression is false
  • assert.fail(actual, expected, [message], [operator]): throws an error with values of actual, expected, and operator
  • assert.ok(object, [message]): throws an error when the object is not double equal (==) to true—aka, truthy (0, and an empty string is false in JavaScript/Node.js)
  • assert.notOk(object, [message]): throws an error when the object is falsy, i.e., false, 0(zero),“”(empty string), null, undefined or NaN
  • assert.equal(actual, expected, [message]): throws an error when actual is not double equal (==) to expected
  • asseret.notEqual(actual, expected, [message]): throws an error when actual is double equal (==)—in other words, not unequal (!=)—to expected
  • .strictEqual(actual, expected, [message]): throws an error when objects are not triple equal (===)

For the full chai assert API, refer to the official documentation (http://chaijs.com/api/assert/).

Note The chai assert (chai.assert) and the Node.js core assert(assert) modules are not 100% compatible, because the former has more methods. The same is true for chai expect and a standalone expect.js.

BDD with Expect.js

Expect.js is one of the BDD languages. Its syntax allows for chaining and is richer in features than core module assert. There are two options to use expect.js:

Install as a local module
Install as a part of the chai library

For the former, simply execute the following:

$ mkdir node_modules
$ npm install expect.js@0.2.0

And, use var expect = require('expect.js'); inside a Node.js test file. For example, the previous test can be rewritten in expect.js BDD style:

    var expect = require('expect.js');
    var expected, current;
    before(function(){
      expected = ['a', 'b', 'c'];
    })
    describe('String#split', function(){
      beforeEach(function(){
        current = 'a,b,c'.split(',');
      })
      it('should return an array', function(){
        expect(Array.isArray(current)).to.be.true;
      });

    it('should return the same array', function(){
      expect(expected.length).to.equal(current.length);
      for (var i=0; i<expected.length; i++) {
        expect(expected[i]).equal(current[i]);
      }
    })
  })

For the chai library approach, run the following:

$ mkdir node_modules
$ npm install chai@1.8.1

And, use var chai = require('chai'); var expect = chai.expect; inside a Node.js test file. For example:

var expect = require('chai').expect;

Note $ mkdir node_modules is needed only if you install NPM modules in the folder that has neither the node_modules directory already nor a package.json file.

Expect.js Syntax

The Expect.js library is very extensive. It has nice methods that mimic natural language. Often there are a few ways to write the same assertion, such as expect(response).to.be(true) and expect(response).equal(true). The following lists some of the main Expect.js methods/properties:

  • ok: checks for truthyness
  • true: checks whether the object is truthy
  • to.be,to: chains methods as in linking two methods
  • not: chains with a not connotation, such as expect(false).not.to.be(true)
  • a/an: checks type (works with array as well)
  • include/contain: checks whether an array or string contains an element
  • below/above: checks for the upper and lower limits

Note Again, there is a slight deviation between the standalone expect.js module and its Chai counterpart.

For the full documentation on chai expect.js, refer to http://chaijs.com/api/bdd/, and for the standalone, refer to https://github.com/LearnBoost/expect.js/.

In this post, we installed Mocha as a command-line tool and learned its options, we wrote simple tests with assert and the expect.js libraries.

In the next post Writing the First BDD Test for Blog, we’ll created the first test for the Blog app by modifying app.js to work as a module. This will be a real example how you can use Mocha in action.

Node Program Lecture

Node Program

Node Program is ideal for people new to Node, and for those who can use some help with JavaScript itself (stuff like “this”, prototype, var, etc.). In the first few lectures, I cover JavaScript fundamentals. Here’s a video of the beginning of one of the previous Node Program trainings: http://youtu.be/JPdTOJXG-hQ. It’s 19 minutes and a part of a six-hour self-study online course.

FREE Sublime Text License ($70)

Are you tired of seeing the Sublime Text 2 registration popup?

Does it annoy you?

Has it ever interrupted you while you were working on something cool?

Here’s your chance to get rid of that pesky popup and help Sublime devs to make their product even bette!

Enter now to win your very own FREE license to Sublime Text 2 (USD $70): http://webapplog.com/giveaways/sublime/.

Hurry up, the giveaway is only until March 10, 12:45pm PT.

PS: Also, if you share the giveaway, then you’ll have 3 more entries to win for each invitee!

In other words, then more you invite the higher the chances.

How To Up Your API Game: DocuSign DevCon

DocuSign DevCon 2015

If you develop apps and work (or plan to work) with business APIs like: DocuSign, Salesforce, Twilio, Dropbox, Runscope, Gradle, then read on. I have some good news for you.

I remember that each time I had to integrate my apps with some third-party service, I was at the mercy of their documentation, samples and support team. Most time those resources were sufficient.

Continue reading

Ask Me Anything Webapplog Hangout

Webapplog Hangout

I want to help YOU. I want to hear and see you this Sunday at 10am PT on Google Hangout. For one hour you can ask me anything.

Here are the benefits of you showing up for the hangout:

  • You can clarify questions you might have after reading Webapplog and my books
  • You can get guidance about getting a job, a career advice, interview&resume tips
  • You can improve your communication and mindset (for business or 9-5 work).
  • You can increase you energy with yoga and paleo lifestyle advice
  • You can create additional income by asking me about publishing books and courses (and marketing them)
  • You can get feedback on the project you’re working on (or a book, or a copy)
  • You can learn what is so great about growth hacking, marketing, selling and copywriting

It’s an absolutely free Google Hangout. So what are you waiting for? Mark your calendars for Sunday 10am PT and save this link—>>>https://plus.google.com/hangouts/_/gyy24q6q3rlshitbdjrrsw7rxya.

PS: Only first 10 participants will be able to get into the Hangout Video Chat.

The End of Node.js?

io.js

There have been a lot of buzz around io.js for the past few months. Basically, io.js is a branched (forked in Git/GitHub terminology) version of Node.js.

One of the main reasons to deviate from the main Node.js project was to speed up the development. This way io.js can have more recent features from Google Chrome V8 and ECMAScript6. Those are good things, right? Then, why there has been so many concerns over the future of Node.js/io.js? Why some people were confused and skeptical?

Continue reading