Testing JavaScript with Chai and Mocha

http://tiny.cc/DLL

(and some other stuff)

Why test JavaScript

If you have any front-end logic including jquery, ajax, promises, conditionals... etc.

These are untested and vulnerable areas of your application that you are not picked up in code coverage.

Mocha

Mocha is a feature-rich JavaScript test framework for browser and Node. 

  • Asynchronous, Synchronous
  • Dynamic Test Generation
  • Custom Reporters
  • Retry Policies
  • Test Analytics 
  • Test interface styles

BDD Interface

const should = require('chai').should()
let jam = { types: [ 'Blueberry', 'Strawberry', 'Raspberry', 'Plum' ] };

describe('Jam', function() {
  before(function() {
    //...
  });
  
  context('Root', function() {
  
    it('Should be an object', function() {
      jam.should.be.an('object');
    });

  });
});

TDD Interface

const assert = chai.assert;
let jam = { types: [ 'Blueberry', 'Strawberry', 'Raspberry', 'Plum' ] };

suite('Jam', function() {
  setup(function() {
    //...
  });
  
  suite('Root', function() {
    
    test('Is an object', function() {
      assert.isObject(jam, 'Jam is an object'); 
    });
    
  });
});

QUNIT Interface

const expect = chai.expect;
let jam = { types: [ 'Blueberry', 'Strawberry', 'Raspberry', 'Plum' ] };

suite('Jam');

test('Root', function() {
  expect(jam).to.be.an('object');
});

Chai

Chai is an assertion library.

  • BDD syntax
  • Integrates with test frameworks
  • Assertion styles

SHOULD

Should allows you to chain result expectations whilst keeping the test assert clean and verbose.​

const should = require('chai').should()

let jam = { types: [ 'Blueberry', 'Strawberry', 'Raspberry', 'Plum' ] };

jam.should.be.an('object');
jam.should.have.property('types').with.lengthOf(4);
jam.types.should.be.an('array');
jam.types.should.be.an('array').that.does.not.include('Peanut Butter');

Expect

Expect covers the same functionality but it does a lot of the chaining for you.

You pass your test object into the expect then chain your result expectations

const expect = chai.expect;

let jam = { types: [ 'Blueberry', 'Strawberry', 'Raspberry', 'Plum' ] };

expect(jam).to.be.an('object');
expect(jam).to.have.property('types').with.lengthOf(4);
expect(jam.types).to.be.an('array');
expect(jam.types).to.be.an('array').that.does.not.include('Peanut Butter');

Assert

Assert is not as chainable as Should or Expect but can handle more complex assertions and is more like the syntax found in other libraries 

const assert = chai.assert;

let jam = { types: [ 'Blueberry', 'Strawberry', 'Raspberry', 'Plum' ] };

assert.isObject(jam, 'Jam is an object'); 
assert.property(jam, 'types');
assert.isArray(jam.types, 'Jam types is an array');
assert.lengthOf(jam.types, 4, '4 items in array');
assert.doesNotHaveAnyKeys(jam.types, 'Peanut Butter');

Stubs and Spies

Testing functions that return jquery elements, http request, data, asynchronous promises, ect... would be pretty difficult without stubs and spies, they allow us to force a response or watch interactions between objects 

Spies

A test spy is a function that records arguments, return value, the value of this and exception thrown (if any) for all its calls. There are two types of spies: Some are anonymous functions, while others wrap methods that already exist in the system under test.

Stubs

Test stubs are functions with pre-programmed behaviour.

They support the all of the Chai assertion library in addition to methods which can be used to alter the stub’s behaviour.

As spies, stubs can be either anonymous, or wrap existing functions. When wrapping an existing function with a stub, the original function is not called.

Plugins

Plugins are lightweight bolt-on's for chai that help extend functionality without changing the assert chains.

Plugins give you continuity across your entire test suite.

Chai has a massive collection of 3rd party and vendor specific plugins.

 

chai.use(chaiHttp);
chai.use(sinonChai);

 

HTTP

Chai HTTP provides an interface for live integration testing web apps using Chai's chain-able asserts this you test server routes.

Promises

"A Promise is a proxy for a value not necessarily known when the promise is created.

It allows you to associate handlers with an asynchronous action's eventual success value or failure reason.

This lets asynchronous methods return values like synchronous methods: instead of immediately returning the final value, the asynchronous method returns a promise to supply the value at some point in the future."

Promises

function longRunningAddition (x, y) {
   return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve(x + y);
        }, 200);
    });
};

let result = calculator.longRunningAddition(10, 5);
result.should.equal(15);

Custom Reporters

Mocha comes with multiple built in reporters to change the console output of the test run

Coverage

There are lots of tools for measuring code coverage in JavaScript they plug into your test framework of choice and output metrics on how many lines and blocks are covered with tests.

 

Let's look at Istanbul, Coverall, Wallaby 

Mocha-Chai-Slides

By jwcnewton

Mocha-Chai-Slides

Mocha and Chai

  • 224