Mocha

Installation

$ npm install -g mocha

Getting Started

$ npm install -g mocha
$ mkdir test
$ $EDITOR test/test.js

Test Program

var assert = require('chai').assert;
describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      assert.equal(-1, [1,2,3].indexOf(5));
      assert.equal(-1, [1,2,3].indexOf(0));
    });
  });
});

Mocha

$  mocha

  Array
    #indexOf()
      ✓ should return -1 when the value is not present


  1 passing (12ms)

chai

  • BDD/TDD Assertion library for node.js and the browser.
$ npm install chai
  • package.json
"devDependencies": {
  "chai": "*",
  "mocha": "*"
}, 

Assertions

  • should.js : BDD styler shown throughout these docs
  • expect.js : expect() style assertions
  • chai : expect(), assert() and should style assertions
  • better-assert : c-style self-documenting assert()
  • unexpected : the extensible BDD assertion toolkit

Synchronous code

describe('Array', function() {
  describe('#indexOf()', function() {
    it('should return -1 when the value is not present', function() {
      [1,2,3].indexOf(5).should.equal(-1);
      [1,2,3].indexOf(0).should.equal(-1);
    });
  });
});

Asynchronous code

describe('User', function() {
  describe('#save()', function() {
    it('should save without error', function(done) {
      var user = new User('Luna');
      user.save(function(err) {
        if (err) throw err;
        done();
      });
    });
  });
});

Dynamically Generating Tests

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

function add() {
  return Array.prototype.slice.call(arguments).reduce(function(prev, curr) {
    return prev + curr;
  }, 0);
}

describe('add()', function() {
  var tests = [
    {args: [1, 2],       expected: 3},
    {args: [1, 2, 3],    expected: 6},
    {args: [1, 2, 3, 4], expected: 10}
  ];

  tests.forEach(function(test) {
    it('correctly adds ' + test.args.length + ' args', function() {
      var res = add.apply(null, test.args);
      assert.equal(res, test.expected);
    });
  });
});

Result

$ mocha

  add()
    ✓ correctly adds 2 args
    ✓ correctly adds 3 args
    ✓ correctly adds 4 args

TDD vs BDD


TDD

  • Test driven Development

inline


Test Suite

var assert = require('assert'),
    factorial = require('../index');

suite('Test', function (){
    setup(function (){
        // Create any objects that we might need
    });

    suite('#factorial()', function (){
        test('equals 1 for sets of zero length', function (){
            assert.equal(1, factorial(0));
        });

        test('equals 1 for sets of length one', function (){
            assert.equal(1, factorial(1));
        });

        test('equals 2 for sets of length two', function (){
            assert.equal(2, factorial(2));
        });

        test('equals 6 for sets of length three', function (){
            assert.equal(6, factorial(3));
        });
    });
});

Code

module.exports = function (n) {
    if (n < 0) return NaN;
    if (n === 0) return 1;

    return n * factorial(n - 1);
};

Mocha TDD

  • suite(), test(), suiteSetup(), suiteTeardown(), setup(), and teardown()
suite('Array', function() {
  setup(function() {
    // ...
  });

  suite('#indexOf()', function() {
    test('should return -1 when not present', function() {
      assert.equal(-1, [1,2,3].indexOf(4));
    });
  });
});

Mocha BDD

  • describe(), context(), it(), specify(), before(), after(), beforeEach(), and afterEach()
describe('Array', function() {
  before(function() {
	// ...
  });

  describe('#indexOf()', function() {
	context('when not present', function() {
	  it('should not throw an error', function() {
		(function() {
		  [1,2,3].indexOf(4);
		}).should.not.throw();
	  });
	  it('should return -1', function() {
		[1,2,3].indexOf(4).should.equal(-1);
	  });
	});
	context('when present', function() {
	  it('should return the index where the element first appears in the array', function() {
		[1,2,3].indexOf(3).should.equal(2);
	  });
	});
  });
});

BDD

  • Behaviour Driven Development
  • BDD is meant to eliminate issues that TDD might cause
    • The ability to read your tests like a sentence is a cognitive shift in how you will think about your tests
  • BDD is to help design the software, not test it like what TDD is meant to do

var assert = require('assert'),
    factorial = require('../index');

describe('Test', function (){
    before(function(){
        // Stuff to do before the tests, like imports, what not
    });

    describe('#factorial()', function (){
        it('should return 1 when given 0', function (){
            factorial(0).should.equal(1);
        });

        it('should return 1 when given 1', function (){
            factorial(1).should.equal(1);
        });

        it('should return 2 when given 2', function (){
            factorial(2).should.equal(2);
        });

        it('should return 6 when given 3', function (){
            factorial(3).should.equal(6);
        });
    });

    after(function () {
        // Anything after the tests have finished
    });
});

TDD vs BDD

  • It depends on if there is an appropriate testing framework for your given target language, what your coworkers are comfortable with, and sometimes other factors.

  • BDD is more about specification then testing

assertEquals(count,5)   // TDD
$(count).should_be(5)   // BDD

TDD

  • define a test set for the unit first
  • implement the unit
  • finally verify that the implementation of the unit makes the tests success

BDD

  • working outside-in, starting from a business or organizational goal
  • using examples to clarify requirements
  • developing and using a ubiquitous language

  • Scenario 1 : Refunded items should be returned to stock
  • Given : a customer previously bought a black sweater from me
  • And : I currently have three black sweater left in stock
  • When : he returns the sweater for a refund
  • Then : I should have four black sweaters in stock
Written on August 2, 2016