测试
测试是开发应用程序中最重要的部分之一。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' })
})
如何检查节点测试
- 通过传递
{only: true}
选项来隔离你的测试
test('should ...', {only: true}, t => ...)
- 运行
node --test
> node --test --test-only --inspect-brk test/<test-file.test.js>
--test-only
指定启用only
选项运行测试--inspect-brk
将启动节点调试器
- 在 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" })
})