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