Skip to content

Instantly share code, notes, and snippets.

@visualjeff
Created October 14, 2016 17:05
Show Gist options
  • Save visualjeff/f6de98d08cce7f7eb14972d1d636c16e to your computer and use it in GitHub Desktop.
Save visualjeff/f6de98d08cce7f7eb14972d1d636c16e to your computer and use it in GitHub Desktop.
Automated testing example for Node.js Hapi apps using Lab
Node.js testing:
1. Use the package lab (also code and sinon).
npm install lab --save-dev
npm install code --save-dev // assertion library
npm install sinon --save-dev
2. Add the following test script to your package.json
For Linux:
"test": "./node_modules/lab/bin/lab -c -L",
For Windows:
"test": "lab -c -L",
3. Create a test directory for your tests.
4. Common imports for you tests:
const Code = require('code'); //assertion library
const Lab = require('lab');
const Sinon = require('sinon');
const Request = require('request');
const lab = exports.lab = Lab.script();
const server = exports.server = require('../index'); //How lab starts your node.js app
const describe = lab.describe;
const it = lab.it;
const expect = Code.expect;
const cookieVal = 'azure-auth=Fe26.2**0ad...'; //Put token here
5. Tests take the format:
describe('Test case name here:', () => {
//simulate a db error. We were using a document database with a rest endpoint.
it('DB error', (done) => {
const stub = Sinon.stub(Request, 'get', (url, callback) => {
callback(new Error('Error: connect ECONNREFUSED'), null, null); //Error message mirrors what the db would return in the event of a connection being refused.
});
//endpoint info
const options = {
method: 'GET',
url: '/api/v1/availableRoles?locationId=001&locationType=Warehouse',
headers: {
Cookie: cookieVal
}
};
//actual test is exercised
server.inject(options, (response) => {
stub.restore();
expect(response.statusCode).to.equal(500);
done();
});
});
//Test for an empty response from the db.
it('Empty response', (done) => {
//db response payload
const dbResp = { 'db_name':'role','doc_count':0,'doc_del_count':0,'update_seq':0,'purge_seq':0,'compact_running':false,'disk_size':79,'data_size':0,'instance_start_time':'1456585969789668','disk_format_version':6,'committed_update_seq':0 };
//Load the response
const stub = Sinon.stub(Request, 'get', (url, callback) => {
callback(null, null, JSON.stringify(dbResp));
});
//endpoint info
const options = {
method: 'GET',
url: '/api/v1/availableRoles?locationId=001&locationType=Warehouse',
headers: {
Cookie: cookieVal
}
};
//actual test is exercised
server.inject(options, (response) => {
stub.restore();
expect(response.statusCode).to.equal(200);
expect(response.result.data).to.be.instanceof(Array);
expect(response.result.data).to.be.empty();
done();
});
});
//test for a validation error
it('Validation error - no query params', (done) => {
server.inject({ url: '/api/v1/availableRoles', headers: { Cookie: cookieVal } }, (response) => {
expect(response.statusCode).to.equal(400);
expect(response.result.errors[0].title).to.equal('Bad Request');
expect(response.result.errors[0].status).to.equal(400);
expect(response.result.errors[0].detail).to.be.not.empty();
done();
});
});
Here two more complicated examples below:
NOTE: These examples dependency on ldapjs for the ldapClientStub:
npm install ldapjs --save
Import ldapjs into your Javascript file:
const Ldap = require('ldapjs');
it('LDAP search error - Depot', (done) => {
const dbResp = { 'total_rows':2,'offset':0,'rows':[
{ 'id':'3386dc099a874451e890ee4a7d003e66','key':'Warehouse','value':{ '_id':'3386dc099a874451e890ee4a7d003e66','_rev':'1-3e470cd2030438bc28539106953ba0ae','name':'ADM','type':'Warehouse','title':'Admin Manager','groups':'[bldtype]ALL[lang]ADM,[bldtype][region][bldnum],[region] [string] Admin,!App-Showcase(USR),!Group-LOC_ALL,!NoAccess-G_DRIVE,!PowerUsers(LOC),!Internet-VEAL,Google App Pilot - Provisioned Users' } },
{ 'id':'3386dc099a874451e890ee4a7d0042b9','key':'Warehouse','value':{ '_id':'3386dc099a874451e890ee4a7d0042b9','_rev':'1-c3a44ad41e1222d323b35164fcf5d55a','name':'AID','type':'Warehouse','title':'Hearing Aid Manager','groups':'[bldtype]ALL[lang]AID,[bldtype][region][bldnum],[region] [string] Hearing Aid,!Group-LOC_ALL,!NoAccess-G_DRIVE,!PowerUsers(LOC),!App-HAC(PWR),!Internet-VEAL,Google App Pilot - Provisioned Users' } }] };
const getStub = Sinon.stub(Request, 'get', (url, callback) => {
callback(null, null, JSON.stringify(dbResp));
});
const logStub = Sinon.stub(Request, 'post', (url, callback) => { //Logger error
callback('hey', null, {});
});
const ldapClientStub = Sinon.stub(Ldap, 'createClient').returns({
on: () => {},
bind: (dn, password, callback) => {
return callback(null, null);
},
search: (searchBaseDn, searchOptions, callback) => {
return callback(new Error('ldapSearchdErr'), null);
}
});
server.inject({ url: '/api/v1/availableRoles?locationId=001&locationType=Depot', headers: { Cookie: cookieVal } }, (response) => {
getStub.restore();
logStub.restore();
ldapClientStub.restore();
expect(response.statusCode).to.equal(500);
expect(response.result.errors[0].title).to.equal('Internal Server Error');
expect(response.result.errors[0].status).to.equal(500);
expect(response.result.errors[0].detail).to.be.not.empty();
done();
});
});
it('Happy Path - None of the Roles in AD', (done) => {
const dbResp = { 'total_rows':2,'offset':0,'rows':[
{ 'id':'3386dc099a874451e890ee4a7d003e66','key':'Warehouse','value':{ '_id':'3386dc099a874451e890ee4a7d003e66','_rev':'1-3e470cd2030438bc28539106953ba0ae','name':'ADM','type':'Warehouse','title':'Admin Manager','groups':'[bldtype]ALL[lang]ADM,[bldtype][region][bldnum],[region] [string] Admin,!App-Showcase(USR),!Group-LOC_ALL,!NoAccess-G_DRIVE,!PowerUsers(LOC),!Internet-VEAL,Google App Pilot - Provisioned Users' } },
{ 'id':'3386dc099a874451e890ee4a7d0042b9','key':'Warehouse','value':{ '_id':'3386dc099a874451e890ee4a7d0042b9','_rev':'1-c3a44ad41e1222d323b35164fcf5d55a','name':'AID','type':'Warehouse','title':'Hearing Aid Manager','groups':'[bldtype]ALL[lang]AID,[bldtype][region][bldnum],[region] [string] Hearing Aid,!Group-LOC_ALL,!NoAccess-G_DRIVE,!PowerUsers(LOC),!App-HAC(PWR),!Internet-VEAL,Google App Pilot - Provisioned Users' } }] };
const getStub = Sinon.stub(Request, 'get', (url, callback) => {
callback(null, null, JSON.stringify(dbResp));
});
const logStub = Sinon.stub(Request, 'post', (url, callback) => {
callback(null, null, 'something');
});
const ldapClientStub = Sinon.stub(Ldap, 'createClient').returns({
on: () => {},
bind: (dn, password, callback) => {
return callback(null, null);
},
unbind: () => {},
search: (searchBaseDn, searchOptions, callback) => {
const ldapSearchRes = {
on: (event, searchCallback) => {
if (event === 'end') {
return searchCallback({ status: 0 });
}
}
};
return callback(null, ldapSearchRes);
}
});
server.inject({ url: '/api/v1/availableRoles?locationId=001&locationType=Warehouse', headers: { Cookie: cookieVal } }, (response) => {
getStub.restore();
logStub.restore();
ldapClientStub.restore();
expect(response.statusCode).to.equal(200);
expect(response.result.data).to.be.instanceof(Array);
expect(response.result.data.length).to.equal(2);
expect(response.result.data[0].id).to.be.not.empty();
expect(response.result.data[0].type).to.equal('availableRoles');
expect(response.result.data[0].attributes.name).to.equal('ADM');
expect(response.result.data[0].attributes.type).to.equal('Warehouse');
expect(response.result.data[0].attributes.title).to.equal('Admin Manager');
expect(response.result.data[0].attributes.groups).to.be.not.empty();
expect(response.result.data[0].attributes.groups).to.equal('[bldtype]ALL[lang]ADM,[bldtype][region][bldnum],[region] [string] Admin,!App-Showcase(USR),!Group-LOC_ALL,!NoAccess-G_DRIVE,!PowerUsers(LOC),!Internet-VEAL,Google App Pilot - Provisioned Users');
expect(response.result.data[1].id).to.be.not.empty();
expect(response.result.data[1].type).to.equal('availableRoles');
expect(response.result.data[1].attributes.name).to.equal('AID');
expect(response.result.data[1].attributes.type).to.equal('Warehouse');
expect(response.result.data[1].attributes.title).to.equal('Hearing Aid Manager');
expect(response.result.data[1].attributes.groups).to.be.not.empty();
expect(response.result.data[1].attributes.groups).to.equal('[bldtype]ALL[lang]AID,[bldtype][region][bldnum],[region] [string] Hearing Aid,!Group-LOC_ALL,!NoAccess-G_DRIVE,!PowerUsers(LOC),!App-HAC(PWR),!Internet-VEAL,Google App Pilot - Provisioned Users');
done();
});
});
});
For details see the lab docs:
https://www.npmjs.com/package/lab
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment