ref – https://ubuverse.com/introduction-to-node-js-api-unit-testing-with-mocha-and-chai/
https://scotch.io/tutorials/test-a-node-restful-api-with-mocha-and-chai
https://gist.github.com/yoavniran/1e3b0162e1545055429e#mocha
https://gist.github.com/yoavniran/1e3b0162e1545055429e#chai
Creating the Directory
mkdir mocha-chai-demo
cd mocha-chai-demo
Setting up the package.json
in directory /mocha-chai-demo:
npm init
use defualt for all the options.
You will then have a package.json file created, which looks something like this:
package name: (mocha-chai-demo) tdd-for-node
version: (1.0.0)
description: test driven dev for node
entry point: (index.js)
test command:
git repository:
keywords: tdd chai mocha node js
author: ricky
license: (ISC)
About to write to /Users/xxxxxxxxx/Desktop/mocha-chai-demo/package.json:
{
“name”: “tdd-for-node”,
“version”: “1.0.0”,
“description”: “test driven dev for node”,
“main”: “index.js”,
“scripts”: {
“test”: “echo \”Error: no test specified\” && exit 1″
},
“keywords”: [
“tdd”,
“chai”,
“mocha”,
“node”,
“js”
],
“author”: “ricky”,
“license”: “ISC”
}
Is this ok? (yes) yes
EPCNSZXW0324:mocha-chai-demo rickytsao$ ls
package.json
Open it with text editor by typing:
open -a TextEdit package.json
and you’ll see your typical specs laid out for your node project.
Whenever you run the project, usually people would do “nodex index.js”. But it can get annoying. So instead, we can shortcut it with start. So next time, all you gotta do to start the server is to type: npm start
You should also edit the test script for mocha.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
{ "name": "krakentest", "version": "1.0.0", "description": "test for using Kraken io", "main": "index.js", "scripts": { "start": "node index.js", // <-- start the server "test": "mocha --recursive" // <-- testing the server }, "keywords": [ "kraken", "io", "image" ], "author": "ricky", "license": "ISC" } |
Under scripts, the value for the key “test” should be: “mocha –recursive”
Implementing the server
in directory /mocha-chai-demo:
touch index.js
Then copy the code below into the index.js
That file will run as our server.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
'use strict'; const express = require('express'); const bodyParser = require('body-parser'); const PORT = 8080; const HOST = 'localhost'; const DEFAULT_COLORS = ['RED', 'GREEN', 'BLUE']; const app = express(); app.use(bodyParser.json({ limit: '100k', })); /** * Array holding color values */ let colors = [].concat(DEFAULT_COLORS); /** * Returns a list of colors * Response body (JSON): {results: [....]} */ app.get('/colors', function(req, res, next) { res.json({ results: colors }); }); /** * Inserts new color in the 'colors' array * Request body (JSON): {color: ...} */ app.post('/colors', function(req, res, next) { if (req.is('application/json') && typeof req.body.color === 'string') { let color = req.body.color.trim().toUpperCase(); if (color && colors.indexOf(color) < 0) { colors.push(color); // 201 Created return res.status(201).send({ results: colors }); } } res.status(400).send(); // 400 Bad Request }); app.listen(PORT, HOST); console.log('Listening on %s:%d...', HOST || '*', PORT); /** * Export the Express app so that it can be used by Chai */ module.exports = app; |
1 2 |
$ npm install express body-parser --save $ npm install mocha chai chai-http --save-dev |
Then run node index.js to see the server in action
If you were to run the app:
node index.js
You’ll see the confirmation messages.
Open up a browser, type: http://localhost:8080/colors
you’ll get {“results”:[“RED”,”GREEN”,”BLUE”]}
Creating the tests
The recommended way to organize your tests within your project is to put all of them in their own /test directory.
By default, Mocha checks for unit tests using the globs ./test/*.js and ./test/*.coffee. From there, it will load and execute any file that calls the describe() method.
In mocha-chai-demo:
mkdir test
cd test
you should now be in /mocha-chai-demo/test
touch test.js
then copy the below test code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
'use strict'; const chai = require('chai'); const expect = require('chai').expect; chai.use(require('chai-http')); const app = require('../index.js'); // Our app describe('API endpoint /colors', function() { this.timeout(5000); // How long to wait for a response (ms) before(function() { }); after(function() { }); // GET - List all colors it('should return all colors', function() { return chai.request(app) .get('/colors') .then(function(res) { expect(res).to.have.status(200); expect(res).to.be.json; expect(res.body).to.be.an('object'); expect(res.body.results).to.be.an('array'); }); }); // GET - Invalid path it('should return Not Found', function() { return chai.request(app) .get('/INVALID_PATH') .then(function(res) { throw new Error('Path exists!'); }) .catch(function(err) { expect(err).to.have.status(404); }); }); // POST - Add new color it('should add new color', function() { return chai.request(app) .post('/colors') .send({ color: 'YELLOW' }) .then(function(res) { expect(res).to.have.status(201); expect(res).to.be.json; expect(res.body).to.be.an('object'); expect(res.body.results).to.be.an('array').that.includes( 'YELLOW'); }); }); // POST - Bad Request it('should return Bad Request', function() { return chai.request(app) .post('/colors') .type('form') .send({ color: 'YELLOW' }) .then(function(res) { throw new Error('Invalid content type!'); }) .catch(function(err) { expect(err).to.have.status(400); }); }); }); |
Save the file.
Then in the test directory, just type:
npm test
You’ll see that we have 4 passed tests.
Details
ref – http://chaijs.com/api/bdd/
Expect
The BDD style is exposed through expect or should interfaces. In both scenarios, you chain together natural language assertions.
1 2 3 4 5 6 7 8 |
var expect = require('chai').expect , foo = 'bar' , beverages = { tea: [ 'chai', 'matcha', 'oolong' ] }; expect(foo).to.be.a('string'); expect(foo).to.equal('bar'); expect(foo).to.have.lengthOf(3); expect(beverages).to.have.property('tea').with.lengthOf(3); |
The should style allows for the same chainable assertions as the expect interface, however it extends each object with a should property to start your chain. This style has some issues when used with Internet Explorer, so be aware of browser compatibility.
Differences between expect and should
First of all, notice that the expect require is just a reference to the expect function, whereas with the should require, the function is being executed.
The expect interface provides a function as a starting point for chaining your language assertions. It works on node.js and in all browsers.
The should interface extends Object.prototype to provide a single getter as the starting point for your language assertions. It works on node.js and in all modern browsers except Internet Explorer.