Writing test for JavaScript with

Testing, 1.. 2.. 3..

Mocha and Chai

Test Driven Development

The idea that you write tests first, then you write code.

As a result, you think about how you will use the code before you implement it.

assert(isEven(2), true)
function isEven(n) {
  return n % 2 === 0;
}

FAIL

PASS

Red, Green, Refactor

Write just enough code

Unit tests should be specific, and so should the code to make the test pass.

Once it passes, refactor, and move on to the next test.

Common pitfalls

  • Forgetting to run tests frequently
  • Writing too many tests at once
  • Writing tests that are too large or coarse-grained
  • Writing overly trivial tests, for instance omitting assertions
  • Writing tests for trivial code, for instance accessors

Mocha is a feature-rich JavaScript testing framework that runs on both Node and in the browser.

describe('add()', function () {

    it('should add two numbers together', function () {
        expect(add(1, 2)).to.equal(3);
    });

    it('should not concatenate a string and a number together', function() {
        expect(add("1", 2)).to.not.equal("12");
    });
    
});

Mocha

$ mocha
function add(x, y) {
  return x + y;
}

Why doesn't this pass all the tests?!

describe('Suite', function () {

  it('should do something', function () {
    // Test code goes here.
  });

  it('should do more things', function() {
    // More test code goes here.
  });

});

There are two important functions when writing tests:

  • describe() –  Groups test cases into test suites.

  • it() –  Individual test cases.

Hooks

Hooks allow you to set up both the state before and after your tests run.

describe('hooks', function() {

  before(function() {
    // runs before all tests in this block
  });

  after(function(){
    // runs after all tests in this block
  });

  beforeEach(function(){
    // runs before each test in this block
  });

  afterEach(function(){
    // runs after each test in this block
  });

  // test cases

})
describe('fillArray', function () {

  var array;

  beforeEach(function () {
    array = [];  // Reset the array before each test runs.
  });

  it('should fill an array with a value, n-times', function () {
    fillArray(array, "Hello", 3);
    expect(array).to.deep.equal(["Hello", "Hello", "Hello"]);
  });

  it('should only fill a positive amount of times', function() {
    fillArray(array, "Hi", -1);
    expect(array).to.be.empty;
  });

});

Let's take a look at the beforeEach()​ hook, which resets the array before each test case is ran.

Skipping tests

Sometimes it's useful to skip tests when you don't know how to implement a test, but know it needs to be done.

describe('shuffleArray', function() {
  it.skip('should be sorted in random order');
});

Text

File watcher support

Automatically runs tests whenever a file is changed.

$ mocha --watch

An assertion library for node and the browser that can be paired with any JavaScript testing framework.

Chai

describe('add()', function() {
  it('should add two numbers together', function(){
    var sum = add(4, 2);
    expect(sum).to.equal(6);
  });
});

Chai!

Should interface

Extends each object with a should property to start your assertion chain.

var foo = 'bar',
    beverages = {tea: [ 'chai', 'matcha', 'oolong']};

foo.should.be.a('string');
foo.should.equal('bar');
foo.should.have.length(3);
beverages.should.have.property('tea').with.length(3);

Expect interface

Chai provides an expect() function which lets you start your assertion chain.

var foo = 'bar',
    beverages = {tea: [ 'chai', 'matcha', 'oolong']};

expect(foo).to.be.a('string');
expect(foo).to.equal('bar');
expect(foo).to.have.length(3);
expect(beverages).to.have.property('tea').with.length(3);

Unlike the should property, the expect function works on values that may be undefined.

Expect interface

Sometimes assertions can be difficult to understand unless you look at the actual test.

describe('turnOn()', function () {

    var lightbulb;

    before(function () {
        lightbulb = { isOn: false };
    });

    it('should turn on a lightbulb', function () {
        turnOn(lightbulb);
        expect(lightbulb.isOn).to.be.true;
    });

});

What is this false value?

Expect interface

expect() allows us to pass in a string to help tests easier to understand at a glance.

describe('turnOn()', function () {

    var lightbulb;

    before(function () {
        lightbulb = { isOn: false };
    });

    it('should turn on a lightbulb', function () {
        turnOn(lightbulb);
        expect(lightbulb.isOn, 'lightbulb.isOn should be true').to.be.true;
    });

});

Language Chains

You may have noticed that these tests read a lot like English.

There are some words Chai provides to "chain" assertions together so that they are easy to read.

  • to
  • be
  • been
  • is
  • that
  • which
  • and
  • has
  • have
  • with
  • at
  • of
  • same

Language Chains

These mean the exact same thing:

expect(1).to.equal(1);
expect(1).equal(1);
expect(1).to.be.that.which.is.to.equal(1);

Equality with Objects and Arrays

Checking for equality between objects and arrays can be tricky in JavaScript.

Equality with Objects and Arrays

Luckily, Chai provides us with deep.equal (or eql), which compares each value inside objects and arrays for us.

var myObject = { name: "Mocha" };
expect(myObject).to.deep.equal({ name: "Mocha" });

var myArray = [1, 2, 3];
expect(myArray).to.eql([1, 2, 3]);

More ways to compare!

There are many more ways to compare values than equality, please read the docs!

  • a
  • above
  • all
  • an
  • any
  • arguments
  • below
  • change
  • changes
  • closeTo
  • contain
  • contains
  • decrease
  • decreases
  • deep
  • deep.equal
  • deep.property
  • empty
  • eq
  • eql
  • eqls
  • equal
  • equals
  • exist
  • false
  • greaterThan
  • gt
  • gte
  • haveOwnProperty
  • include
  • includes
  • increase
  • increases
  • instanceof
  • itself
  • key
  • keys
  • least
  • length
  • lengthOf
  • lessThan
  • lt
  • lte
  • match
  • members
  • most
  • not
  • null
  • ok
  • ownProperty

...plus more!

(But seriously, write them sooner)

Testing, 1.. 2.. 3...

By Tony Gaskell

Testing, 1.. 2.. 3...

Writing tests for JavaScript using Mocha and Chai

  • 2,065