Fastify

测试

测试是开发应用最重要的一部分。Fastify 处理测试非常灵活并且它兼容绝大多数框架 (例如 Tap。下面的例子都会用这个演示)。

让我们 cd 进入一个全新的 ‘testing-example’ 文件夹,并在终端里输入 npm init -y

执行 npm install fastify && npm install tap pino-pretty --save-dev

关注点分离让测试变得轻松

首先,我们将应用代码与服务器代码分离:

app.js:

  1. 'use strict'
  2. const fastify = require('fastify')
  3. function build(opts={}) {
  4. const app = fastify(opts)
  5. app.get('/', async function (request, reply) {
  6. return { hello: 'world' }
  7. })
  8. return app
  9. }
  10. module.exports = build

server.js:

  1. 'use strict'
  2. const server = require('./app')({
  3. logger: {
  4. level: 'info',
  5. prettyPrint: true
  6. }
  7. })
  8. server.listen(3000, (err, address) => {
  9. if (err) {
  10. console.log(err)
  11. process.exit(1)
  12. }
  13. })

使用 fastify.inject() 的好处

感谢有 light-my-request,Fastify 自带了伪造的 HTTP 注入。

在进行任何测试之前,我们通过 .inject 方法向路由发送假的请求:

app.test.js:

  1. 'use strict'
  2. const build = require('./app')
  3. const test = async () => {
  4. const app = build()
  5. const response = await app.inject({
  6. method: 'GET',
  7. url: '/'
  8. })
  9. console.log('status code: ', response.statusCode)
  10. console.log('body: ', response.body)
  11. }
  12. test()

我们的代码运行在异步函数里,因此可以使用 async/await。

.inject 确保了所有注册的插件都已引导完毕,可以开始测试应用了。之后请求方法将被传递到路由函数中去。使用 await 可以存储响应,且避免了回调函数。

在终端执行 node app.test.js 来开始测试。

  1. status code: 200
  2. body: {"hello":"world"}

HTTP 注入测试

现在我们能用真实的测试语句代替 console.log 了!

package.json 里修改 “test” script 如下:

"test": "tap --reporter=list --watch"

app.test.js:

  1. 'use strict'
  2. const { test } = require('tap')
  3. const build = require('./app')
  4. test('requests the "/" route', async t => {
  5. const app = build()
  6. const response = await app.inject({
  7. method: 'GET',
  8. url: '/'
  9. })
  10. t.equal(response.statusCode, 200, 'returns a status code of 200')
  11. })

执行 npm test,查看结果!

inject 方法能完成的不只有简单的 GET 请求:

  1. fastify.inject({
  2. method: String,
  3. url: String,
  4. query: Object,
  5. payload: Object,
  6. headers: Object,
  7. cookies: Object
  8. }, (error, response) => {
  9. // 你的测试
  10. })

忽略回调函数,可以链式调用 .inject 提供的方法:

  1. fastify
  2. .inject()
  3. .get('/')
  4. .headers({ foo: 'bar' })
  5. .query({ foo: 'bar' })
  6. .end((err, res) => { // 调用 .end 触发请求
  7. console.log(res.payload)
  8. })

或是用 promise 的版本

  1. fastify
  2. .inject({
  3. method: String,
  4. url: String,
  5. query: Object,
  6. payload: Object,
  7. headers: Object,
  8. cookies: Object
  9. })
  10. .then(response => {
  11. // 你的测试
  12. })
  13. .catch(err => {
  14. // 处理错误
  15. })

Async await 也是支持的!

  1. try {
  2. const res = await fastify.inject({ method: String, url: String, payload: Object, headers: Object })
  3. // 你的测试
  4. } catch (err) {
  5. // 处理错误
  6. }

另一个例子:

app.js

  1. const Fastify = require('fastify')
  2. function buildFastify () {
  3. const fastify = Fastify()
  4. fastify.get('/', function (request, reply) {
  5. reply.send({ hello: 'world' })
  6. })
  7. return fastify
  8. }
  9. module.exports = buildFastify

test.js

  1. const tap = require('tap')
  2. const buildFastify = require('./app')
  3. tap.test('GET `/` route', t => {
  4. t.plan(4)
  5. const fastify = buildFastify()
  6. // 在测试的最后,我们强烈建议你调用 `.close()`
  7. // 方法来确保所有与外部服务的连接被关闭。
  8. t.teardown(() => fastify.close())
  9. fastify.inject({
  10. method: 'GET',
  11. url: '/'
  12. }, (err, response) => {
  13. t.error(err)
  14. t.equal(response.statusCode, 200)
  15. t.equal(response.headers['content-type'], 'application/json; charset=utf-8')
  16. t.same(response.json(), { hello: 'world' })
  17. })
  18. })

测试正在运行的服务器

你还可以在 fastify.listen() 启动服务器之后,或是 fastify.ready() 初始化路由与插件之后,进行 Fastify 的测试。

举例:

使用之前例子的 app.js

test-listen.js (用 Request 测试)

  1. const tap = require('tap')
  2. const request = require('request')
  3. const buildFastify = require('./app')
  4. tap.test('GET `/` route', t => {
  5. t.plan(5)
  6. const fastify = buildFastify()
  7. t.teardown(() => fastify.close())
  8. fastify.listen(0, (err) => {
  9. t.error(err)
  10. request({
  11. method: 'GET',
  12. url: 'http://localhost:' + fastify.server.address().port
  13. }, (err, response, body) => {
  14. t.error(err)
  15. t.equal(response.statusCode, 200)
  16. t.equal(response.headers['content-type'], 'application/json; charset=utf-8')
  17. t.same(JSON.parse(body), { hello: 'world' })
  18. })
  19. })
  20. })

test-ready.js (用 SuperTest 测试)

  1. const tap = require('tap')
  2. const supertest = require('supertest')
  3. const buildFastify = require('./app')
  4. tap.test('GET `/` route', async (t) => {
  5. const fastify = buildFastify()
  6. t.teardown(() => fastify.close())
  7. await fastify.ready()
  8. const response = await supertest(fastify.server)
  9. .get('/')
  10. .expect(200)
  11. .expect('Content-Type', 'application/json; charset=utf-8')
  12. t.same(response.body, { hello: 'world' })
  13. })

如何检测 tap 的测试

  1. 设置 {only: true} 选项,将需要检测的测试与其他测试分离
  1. test('should ...', {only: true}, t => ...)
  1. 通过 npx 运行 tap
  1. > npx tap -O -T --node-arg=--inspect-brk test/<test-file.test.js>
  • -O 表示开启 only 选项,只运行设置了 {only: true} 的测试
  • -T 表示不设置超时
  • --node-arg=--inspect-brk 会启动 node 调试工具
  1. 在 VS Code 中创建并运行一个 Node.js: Attach 调试配置,不需要额外修改。

现在你便可以在编辑器中检测你的测试文件 (以及 Fastify 的其他部分) 了。