Skip to content

Instantly share code, notes, and snippets.

@Raynos
Created June 8, 2012 16:04
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Raynos/2896455 to your computer and use it in GitHub Desktop.
Save Raynos/2896455 to your computer and use it in GitHub Desktop.
Mocha BDD Test Guide

Guide for writing mocha tests

Prerequites

  • 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

Writing tests

We have describe and it.

describe

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

it

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

Unit tests with sinon

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
    }
}

unit test file level setup

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()
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment