Documentation

Testing

Testing is one of the most important part when you are developing an application. Fastify does not offer a testing framework out of the box, but we can recommend you an handy and nice way to build your unit testing environment.

The modules you'll need:

  • Tap: an excellent testing framework, it will give you out of the box a very good assertion library and a lot utilities.
  • Request: a complete library to perform request of any kind.
  • Minimist: a CLI parser, that you will use to run server from the command line.
// server.js
const minimist = require('minimist')
const Fastify = require('fastify')

const options = {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  }
}

function start (opts, callback) {
  const fastify = Fastify()

  fastify.get('/', options, function (request, reply) {
    reply.send({ hello: 'world' })
  })

  fastify.listen(opts.port, function (err) {
    callback(err, fastify)
  })
}

// In this way you can run the server both from the CLI and as a required module.
if (require.main === module) {
  // Run the server with:
  // $ node server.js -p 8080
  start(minimist(process.argv.slice(2), {
    integer: ['port'],
    alias: {
      port: 'p'
    },
    default: {
      port: 3000
    }
  }), (err, instance) => {
    if (err) throw err

    console.log(`server listening on ${instance.server.address().port}`)
  })
}

// Here we are exposing the function that starts the server
// in this way inside the test files we can require and run it.
module.exports = { start }
// test.js
const t = require('tap')
const test = t.test
const request = require('request')
const server = require('./server')

// Run the server
server.start({ port: 0 }, (err, fastify) => {
  t.error(err)

  test('The server should start', t => {
    t.plan(4)
    // Perform the request
    request({
      method: 'GET',
      uri: `http://localhost:${fastify.server.address().port}`
    }, (err, response, body) => {
      // Unit test
      t.error(err)
      t.strictEqual(response.statusCode, 200)
      t.strictEqual(response.headers['content-length'], '' + body.length)
      t.deepEqual(JSON.parse(body), { hello: 'world' })
      fastify.close()
    })
  })
})

Testing with http injection

Fastify supports fake http injection thanks to light-my-request.

You just need to use the api inject:

fastify.inject({
  method: String,
  url: String,
  payload: Object,
  headers: Object
}, response => {
  // your tests
})

or in the promisified version

fastify
  .inject({
    method: String,
    url: String,
    payload: Object,
    headers: Object
  })
  .then(response => {
    // your tests
  })

Async await is supported as well!

const res = await fastify.inject({ method: String, url: String, payload: Object, headers: Object })

Example:

// server.js
const minimist = require('minimist')
const fastify = require('fastify')()

const options = {
  schema: {
    response: {
      200: {
        type: 'object',
        properties: {
          hello: { type: 'string' }
        }
      }
    }
  }
}

fastify.get('/', options, function (request, reply) {
  reply.send({ hello: 'world' })
})

function start (opts, callback) {
  fastify.listen(opts.port, function (err) {
    callback(err, fastify)
  })
}

// In this way you can run the server both from the CLI and as a required module.
if (require.main === module) {
  // Run the server with:
  // $ node server.js -p 8080
  start(minimist(process.argv.slice(2), {
    integer: ['port'],
    alias: {
      port: 'p'
    },
    default: {
      port: 3000
    }
  }), (err, instance) => {
    if (err) throw err

    console.log(`server listening on ${instance.server.address().port}`)
  })
}

// note that now we are also exposing the fastify instance
module.exports = { start, fastify }
// test.js
const t = require('tap')
const test = t.test
const fastify = require('./server').fastify

test('GET `/` route', t => {
  t.plan(3)

  fastify.inject({
    method: 'GET',
    url: '/'
  }, res => {
    t.strictEqual(res.statusCode, 200)
    t.strictEqual(res.headers['content-length'], '' + res.payload.length)
    t.deepEqual(JSON.parse(res.payload), { hello: 'world' })
    // even if the server is not running (inject does not run the server)
    // at the end of your tests is highly recommended call `.close()`,
    // in this way you will close all the connections to external services
    fastify.close()
  })
})