- Use BDD interface
- Use sinon for unit test mocking
- Use request for HTTP tests
Examples inspired by templar-hogan integration tests and templar-hogan unit tests
We have describe
and it
.
describe
is used to describe an action. A top level describe
can be used to describe the entire suite with a single word that can be grepped.
describe("Templar-hogan server", function () {
...
})
For all other describe
calls describe an action.
For example
describe("calling /templar", ...)
Inside a describe
block do the action you describe in the before
block.
describe("calling /templar", function () {
before(function (done) {
// makeRequest is a utility function
makeRequest("/templar", done)
})
})
Inside your it
blocks you only do assertions and not the action your describing. For you to make assertions you need the results or data you acted upon. This can be done by having closure scoped variables.
describe("Templar-hogan", function () {
var err, res, body
describe("calling /templar", function () {
before(function (done) {
request({
uri: "http://localhost:8080/templar"
}, function (_err, _res, _body) {
err = _err
res = _res
body = _body
done()
})
})
/* assertions here in it blocks */
})
...
})
It should be noted that exporting tokens up the closure scope may look ugly but is a necessary evil until a better pattern is established.
It should also be noted that because the closure scope data is in an outer function scope the before code can be refactored into a makeRequest function
An it
block simply contains assertions on objects defined up the closure scope.
it("should have status code 200", function () {
assert.equal(res.statusCode, 200, "statusCode is not 200")
})
If you use closure variables correctly and move them up high enough into scope it will allow you to refactor the entire function passed to it into a seperate function
Re-using the function to it in multiple tests is the technique used for writing DRY tests
When writing unit tests the before action should do some setup logic of the mocks and then the action
var routil, templar, options,
uri = {}
describe("calling hoganize", function () {
before(function () {
setup()
options = hoganize(templar, uri)
})
/* it blocks */
})
function setup() {
templar = createTemplar()
routil = createRoutil(templar)
}
function createRoutil(templar) {
var config = sinon.spy()
return {
config: config,
Templar: templar
}
}
function createTemplar() {
var loadFolder = sinon.spy()
return {
loadFolder: loadFolder
}
}
At the top level describe you may want to do some setup before like
describe("top level name", function () {
before(function (done) {
// require("../app")(done)
// require("ncore")(done)
http.createServer(dummyHttpHandler).listen(8080, done)
})
})
This should be done at the top level and it should reset all state including making sure any database is in the correct neutral state at startup.
The before logic in other describes may want to do more local setting state correctly.
You should also include after blocks for correct shutdown
after(function (done) {
server.on("close", done)
server.close()
})