Brief
Started a mad API but, when the next step is wanted I got a problem. How is the better approach to testing my API? Mocha? Jasmine? Wows?
In Hapi ecosystem we have Lab a powerful and clean engine for you test you api made easy. For this propose we'll focused in e2e(end to end) tests for our MAD API.
Proposes Mad API
Just for this example proposes, imagine a simple API with just one endpoint called TODO.
The TODO endpoint, have the methods LIST, GET, CREATE, UPDATE and DESTROY just another REST API with a **CRUD(**Create, Read, Update, Delete).
Tools
We will just use Lab and Chai. Chai is an Assertion library. We've some other tools like: Should, Code and the standard Assertion library in Node. But I'm more close with Chai and this library help me a lot because I can use promises in some cases and other cool stuffs in Chai just installing a Chai plugin.
Code is good and are in Hapi organization in Github. Feel free to choose the assertion library you're more confident.
Inside your Laboratory
I like creating an simple index.spec.js in my tests root directory for bootstrapping your dependencies and waiting when database is connected.
'use strict'; | |
// load deps | |
let lab = exports.lab = require('lab').script(); | |
global.expect = require('chai').expect; | |
// prepare environment | |
global. it = lab.it; | |
global.describe = lab.describe; | |
global.before = lab.before; | |
global.beforeEach = lab.beforeEach; | |
// get the server | |
global.server = require('../lib/server'); | |
global.db = global.server.database; | |
global.before((done) => [ | |
global.db['database'].on('connected', () => { | |
done(); | |
}) | |
]); |
In my case I have some Globals, sorry for that. :(
Lab is inspired by Mocha and your simplicity, we have some different method in Lab too, but in my case I choose have a similar interface like Mocha for keep the simplicity.
The really cool goal is in line #13 when we require our server. In this file we just require and load plugins and interfaces required for running our server and export the instance of Hapi server.
After this, we can write our code like Mocha applications, but with the powers of *server.inject() *function. (just waiting for few more lines)
This source above is an snippet extracted from my project for illustrate with more fidelity a real test.
/* global describe, beforeEach, before, it, expect, db, server */ | |
'use strict'; | |
describe('Routes /todo', () => { | |
let token; | |
before((done) => { | |
let options = { | |
method: 'POST', | |
url: '/user', | |
payload: { | |
name: 'Jack Bauer', | |
username: 'jack_b', | |
email: 'jbauer@24hours.com', | |
password: '#24hoursRescuePresident' | |
} | |
}; | |
server.inject(options, (response) => { | |
token = response.result.token; | |
done(); | |
}); | |
}); | |
describe('GET /todo', () => { | |
it('return 200 HTTP status code', (done) => { | |
db.Todo.remove(() => { | |
let options = { | |
method: 'GET', | |
url: '/todo', | |
headers: {'Authorization': 'Bearer ' + token} | |
}; | |
server.inject(options, (response) => { | |
expect(response).to.have.property('statusCode', 200); | |
done(); | |
}); | |
}); | |
}); | |
it('returns an empty array when todo is empty', (done) => { | |
db.Todo.remove(() => { | |
let options = { | |
method: 'GET', | |
url: '/todo', | |
headers: {'Authorization': 'Bearer ' + token} | |
}; | |
server.inject(options, (response) => { | |
expect(response).to.have.property('result'); | |
expect(response.result).to.have.length.least(0); | |
done(); | |
}); | |
}); | |
}); | |
it('return 1 todo at a time', (done) => { | |
let options = { | |
method: 'GET', | |
url: '/todo', | |
headers: {'Authorization': 'Bearer ' + token} | |
}; | |
server.inject(options, (response) => { | |
expect(response).to.have.property('result'); | |
expect(response.result).to.have.length.least(1); | |
let todo = response.result; | |
expect(todo).to.have.property('name'); | |
expect(todo.name).to.contain('TODO Task'); | |
expect(todo).to.have.property('checked', false); | |
done(); | |
}); | |
}); | |
}); | |
}); |
Note, in this case, we can write our tests like a mocha tests, because we previously export functions like mocha for testing.
The coolest part is the server.inject function because we can call our web server up and running in our tests. Just like the module request but this feature is provided by wreck.
So, the rest of our tests is very like any other e2e tests for API, calling endpoint, wait the correct data and be hapi =D
I hide a lot of the implementation for this snippet and you can see the entire source here.
So the next step is running our tests. And lab beyond mocha features, it have the features of istanbul for code coverage too and show you possible leaks and the lines you not passed directly in your terminal.
npm test
After running npm test I got this output. Total of tests complete, duration, percent of coveraged source and the lines my tests are not coveraged.
But, just running npm test will not ouput this directly you need say to npm run lab.
./node_modules/.bin/lab -c -t 90 -v -l
- c — This is the flag to say "Lab run the coverage!"
- t 90 — This say to lab "My minimum coverage is 90%"
- v — This say to lab "Please, run my tests verbose!"
- l — This is specific in my test cases, say "Ignore globals variable leaks detection"
Lab has a lot of more options, but I like using just these.
Conclusion
Well, this is an little snippet about how I use lab in my Mad APIs and pet projects. Isn't a full complete approach of tests with lab, but is wanna be a start point for who wants more information about how you can tests your Hapi api with lab.
Feel free to help me to improve this post send me feedbacks in comments =D