Skip to Content

测试

测试是开发应用程序中最重要的部分之一。Fastify 在测试方面非常灵活,并且与大多数测试框架兼容(例如 Node Test Runner ,以下示例中使用了该框架)。

应用程序

让我们进入一个名为 ‘testing-example’ 的新目录,并在终端中输入 npm init -y

运行 npm i fastify && npm i pino-pretty -D

分离关注点使测试变得容易

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

app.js:

'use strict' const fastify = require('fastify') function build(opts={}) { const app = fastify(opts) app.get('/', async function (request, reply) { return { hello: 'world' } }) return app } module.exports = build

server.js:

'use strict' const server = require('./app')({ logger: { level: 'info', transport: { target: 'pino-pretty' } } }) server.listen({ port: 3000 }, (err, address) => { if (err) { server.log.error(err) process.exit(1) } })

使用 fastify.inject() 的好处

Fastify 内置了对假 HTTP 注入的支持,这要归功于 light-my-request

在引入任何测试之前,我们将使用 .inject 方法向我们的路由发送一个假请求:

app.test.js:

'use strict' const build = require('./app') const test = async () => { const app = build() const response = await app.inject({ method: 'GET', url: '/' }) console.log('状态码:', response.statusCode) console.log('响应体:', response.body) } test()

首先,我们的代码将在异步函数中运行,使我们能够使用 async/await。

.inject 确保所有已注册的插件已经启动,并且应用程序准备好进行测试。最后,我们将要使用的请求方法和路由传递给它。通过使用 await,我们可以存储响应而无需回调。

在终端中运行测试文件 node app.test.js

状态码: 200 响应体: {"hello":"world"}

使用HTTP注入进行测试

现在我们可以用实际的测试来替换console.log调用了!

在你的 package.json 中将 “test” 脚本改为:

"test": "node --test --watch"

app.test.js:

'use strict' const { test } = require('node:test') const build = require('./app') test('请求 "/" 路由', async t => { t.plan(1) const app = build() const response = await app.inject({ method: 'GET', url: '/' }) t.assert.strictEqual(response.statusCode, 200, '返回状态码为 200') })

最后,在终端中运行 npm test 并查看你的测试结果!

inject 方法可以做比简单的 GET 请求到 URL 更多的事情:

fastify.inject({ method: String, url: String, query: Object, payload: Object, headers: Object, cookies: Object }, (error, response) => { // 你的测试代码 })

.inject 方法也可以通过省略回调函数来链接:

fastify .inject() .get('/') .headers({ foo: 'bar' }) .query({ foo: 'bar' }) .end((err, res) => { // 调用 .end 将触发请求 console.log(res.payload) })

或者在 Promise 版本中:

fastify .inject({ method: String, url: String, query: Object, payload: Object, headers: Object, cookies: Object }) .then(response => { // 你的测试代码 }) .catch(err => { // 处理错误 })

异步/等待也得到了支持!

try { const res = await fastify.inject({ method: String, url: String, payload: Object, headers: Object }) // 你的测试代码 } catch (err) { // 处理错误 }

另一个示例:

app.js

const Fastify = require('fastify') function buildFastify () { const fastify = Fastify() fastify.get('/', function (request, reply) { reply.send({ hello: 'world' }) }) return fastify } module.exports = buildFastify

test.js

const { test } = require('node:test') const buildFastify = require('./app') test('GET `/` 路由', t => { t.plan(4) const fastify = buildFastify() // 在测试结束时强烈建议调用 `.close()` // 确保所有对外部服务的连接都被关闭。 t.after(() => fastify.close()) fastify.inject({ method: 'GET', url: '/' }, (err, response) => { t.assert.ifError(err) t.assert.strictEqual(response.statusCode, 200) t.assert.strictEqual(response.headers['content-type'], 'application/json; charset=utf-8') t.assert.deepStrictEqual(response.json(), { hello: 'world' }) }) })

使用运行中的服务器进行测试

在使用 fastify.listen() 启动服务器后,或在使用 fastify.ready() 初始化路由和插件后,也可以对 Fastify 进行测试。

示例:

使用了前一个示例中的 app.js

test-listen.js(使用 undici 进行测试)

const { test } = require('node:test') const { Client } = require('undici') const buildFastify = require('./app') test('应与 undici 一起工作', async t => { t.plan(2) const fastify = buildFastify() await fastify.listen() const client = new Client( 'http://localhost:' + fastify.server.address().port, { keepAliveTimeout: 10, keepAliveMaxTimeout: 10 } ) t.after(() => { fastify.close() client.close() }) const response = await client.request({ method: 'GET', path: '/' }) t.assert.strictEqual(await response.body.text(), '{"hello":"world"}') t.assert.strictEqual(response.statusCode, 200) })

或者,从 Node.js 18 开始, fetch 可以在不引入额外依赖的情况下使用:

test-listen.js

const { test } = require('node:test') const buildFastify = require('./app') test('应与 fetch 一起工作', async t => { t.plan(3) const fastify = buildFastify() t.after(() => fastify.close()) await fastify.listen() const response = await fetch( 'http://localhost:' + fastify.server.address().port ) t.assert.strictEqual(response.status, 200) t.assert.strictEqual( response.headers.get('content-type'), 'application/json; charset=utf-8' ) const jsonResult = await response.json() t.assert.strictEqual(jsonResult.hello, 'world') })

test-ready.js (使用 SuperTest 进行测试)

const { test } = require('node:test') const supertest = require('supertest') const buildFastify = require('./app') test('GET `/` 路由', async (t) => { const fastify = buildFastify() t.after(() => fastify.close()) await fastify.ready() const response = await supertest(fastify.server) .get('/') .expect(200) .expect('Content-Type', 'application/json; charset=utf-8') t.assert.deepStrictEqual(response.body, { hello: 'world' }) })

如何检查节点测试

  1. 通过传递 {only: true} 选项来隔离你的测试
test('should ...', {only: true}, t => ...)
  1. 运行 node --test
> node --test --test-only --inspect-brk test/<test-file.test.js>
  • --test-only 指定启用 only 选项运行测试
  • --inspect-brk 将启动节点调试器
  1. 在 VS Code 中,创建并启动一个 Node.js: Attach 调试配置。通常不需要进行任何修改。

现在你应该能够在代码编辑器中逐步执行你的测试文件(以及 Fastify 的其余部分)。

插件

让我们进入一个名为 ‘testing-plugin-example’ 的新目录,并在终端中输入 npm init -y

运行 npm i fastify fastify-plugin

plugin/myFirstPlugin.js:

const fP = require("fastify-plugin") async function myPlugin(fastify, options) { fastify.decorateRequest("helloRequest", "Hello World") fastify.decorate("helloInstance", "Hello Fastify Instance") } module.exports = fP(myPlugin)

一个基本的插件示例。参见 插件指南

test/myFirstPlugin.test.js:

const Fastify = require("fastify"); const { test } = require("node:test"); const myPlugin = require("../plugin/myFirstPlugin"); test("测试插件路由", async t => { // 创建一个模拟的 fastify 应用程序以测试插件 const fastify = Fastify() fastify.register(myPlugin) // 添加你选择的端点 fastify.get("/", async (request, reply) => { return ({ message: request.helloRequest }) }) // 使用 fastify.inject 模拟 HTTP 请求 const fastifyResponse = await fastify.inject({ method: "GET", url: "/" }) console.log('状态码:', fastifyResponse.statusCode) console.log('响应体:', fastifyResponse.body) })

了解更多关于 fastify.inject()。 在终端中运行测试文件 node test/myFirstPlugin.test.js

状态码:200 响应体:{"message":"Hello World"}

现在我们可以用实际的测试来替换 console.log 调用了!

在你的 package.json 中将 "test" 脚本改为:

"test": "node --test --watch"

创建端点的测试。

test/myFirstPlugin.test.js:

const Fastify = require("fastify"); const { test } = require("node:test"); const myPlugin = require("../plugin/myFirstPlugin"); test("测试插件路由", async t => { // 指定测试数量 t.plan(2) const fastify = Fastify() fastify.register(myPlugin) fastify.get("/", async (request, reply) => { return ({ message: request.helloRequest }) }) const fastifyResponse = await fastify.inject({ method: "GET", url: "/" }) t.assert.strictEqual(fastifyResponse.statusCode, 200) t.assert.deepStrictEqual(JSON.parse(fastifyResponse.body), { message: "Hello World" }) })

最后,在终端中运行 npm test 并查看你的测试结果!

测试 .decorate().decorateRequest()

test/myFirstPlugin.test.js:

const Fastify = require("fastify"); const { test }= require("node:test"); const myPlugin = require("../plugin/myFirstPlugin"); test("测试插件路由", async t => { t.plan(5) const fastify = Fastify() fastify.register(myPlugin) fastify.get("/", async (request, reply) => { // 测试 fastify 装饰器 t.assert.ifError(request.helloRequest) t.assert.ok(request.helloRequest, "Hello World") t.assert.ok(fastify.helloInstance, "Hello Fastify Instance") return ({ message: request.helloRequest }) }) const fastifyResponse = await fastify.inject({ method: "GET", url: "/" }) t.assert.strictEqual(fastifyResponse.statusCode, 200) t.assert.deepStrictEqual(JSON.parse(fastifyResponse.body), { message: "Hello World" }) })