测试
¥Testing
测试是开发应用最重要的部分之一。Fastify 在测试方面非常灵活,并且与大多数测试框架兼容(例如下面示例中使用的 节点测试运行器)。
¥Testing is one of the most important parts of developing an application. Fastify is very flexible when it comes to testing and is compatible with most testing frameworks (such as Node Test Runner, which is used in the examples below).
应用
¥Application
让我们进入一个名为 'testing-example' 的新目录,然后在终端中输入 npm init -y
。
¥Let's cd
into a fresh directory called 'testing-example' and type npm init
-y
in our terminal.
运行 npm i fastify && npm i pino-pretty -D
¥Run npm i fastify && npm i pino-pretty -D
分离关注点使测试变得容易
¥Separating concerns makes testing easy
首先,我们要将应用代码与服务器代码分开:
¥First, we are going to separate our application code from our server code:
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() 的好处
¥Benefits of using fastify.inject()
由于 light-my-request
,Fastify 内置了对伪造 HTTP 注入的支持。
¥Fastify comes with built-in support for fake HTTP injection thanks to
light-my-request
.
在引入任何测试之前,我们将使用 .inject
方法向我们的路由发出虚假请求:
¥Before introducing any tests, we will use the .inject
method to make a fake
request to our route:
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('status code: ', response.statusCode)
console.log('body: ', response.body)
}
test()
首先,我们的代码将在异步函数内运行,使我们能够访问 async/await。
¥First, our code will run inside an asynchronous function, giving us access to async/await.
.inject
确保所有注册的插件都已启动,并且我们的应用已准备好进行测试。最后,我们传递我们想要使用的请求方法和路由。使用 await 我们可以存储响应而不需要回调。
¥.inject
ensures all registered plugins have booted up and our application is
ready to test. Finally, we pass the request method we want to use and a route.
Using await we can store the response without a callback.
在你的终端 node app.test.js
中运行测试文件
¥Run the test file in your terminal node app.test.js
status code: 200
body: {"hello":"world"}
使用 HTTP 注入进行测试
¥Testing with HTTP injection
现在我们可以用实际测试替换我们的 console.log
调用!
¥Now we can replace our console.log
calls with actual tests!
在你的 package.json
中,将 "test" 脚本更改为:
¥In your package.json
change the "test" script to:
"test": "node --test --watch"
app.test.js:
'use strict'
const { test } = require('node:test')
const build = require('./app')
test('requests the "/" route', async t => {
t.plan(1)
const app = build()
const response = await app.inject({
method: 'GET',
url: '/'
})
t.assert.strictEqual(response.statusCode, 200, 'returns a status code of 200')
})
最后,在终端中运行 npm test
并查看你的测试结果!
¥Finally, run npm test
in the terminal and see your test results!
inject
方法可以做的远不止对 URL 的简单 GET 请求:
¥The inject
method can do much more than a simple GET request to a URL:
fastify.inject({
method: String,
url: String,
query: Object,
payload: Object,
headers: Object,
cookies: Object
}, (error, response) => {
// your tests
})
.inject
方法也可以通过省略回调函数进行链接:
¥.inject
methods can also be chained by omitting the callback function:
fastify
.inject()
.get('/')
.headers({ foo: 'bar' })
.query({ foo: 'bar' })
.end((err, res) => { // the .end call will trigger the request
console.log(res.payload)
})
或者在 promise 的版本中
¥or in the promisified version
fastify
.inject({
method: String,
url: String,
query: Object,
payload: Object,
headers: Object,
cookies: Object
})
.then(response => {
// your tests
})
.catch(err => {
// handle error
})
还支持异步等待!
¥Async await is supported as well!
try {
const res = await fastify.inject({ method: String, url: String, payload: Object, headers: Object })
// your tests
} catch (err) {
// handle error
}
另一个例子:
¥Another Example:
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 `/` route', t => {
t.plan(4)
const fastify = buildFastify()
// At the end of your tests it is highly recommended to call `.close()`
// to ensure that all connections to external services get closed.
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' })
})
})
使用正在运行的服务器进行测试
¥Testing with a running server
Fastify 还可以在使用 fastify.listen()
启动服务器后或使用 fastify.ready()
初始化路由和插件后进行测试。
¥Fastify can also be tested after starting the server with fastify.listen()
or
after initializing routes and plugins with fastify.ready()
.
示例:
¥Example:
使用上一个示例中的 app.js。
¥Uses app.js from the previous example.
test-listen.js(使用 undici
进行测试)
¥test-listen.js (testing with undici
)
const { test } = require('node:test')
const { Client } = require('undici')
const buildFastify = require('./app')
test('should work with 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
而无需任何额外的依赖:
¥Alternatively, starting with Node.js 18,
fetch
may be used without requiring any extra dependencies:
test-listen.js
const { test } = require('node:test')
const buildFastify = require('./app')
test('should work with 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
进行测试)
¥test-ready.js (testing with
SuperTest
)
const { test } = require('node:test')
const supertest = require('supertest')
const buildFastify = require('./app')
test('GET `/` route', 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' })
})
如何检查节点测试
¥How to inspect node tests
通过传递
{only: true}
选项隔离你的测试¥Isolate your test by passing the
{only: true}
option
test('should ...', {only: true}, t => ...)
运行
node --test
¥Run
node --test
> node --test --test-only --node-arg=--inspect-brk test/<test-file.test.js>
--test-only
指定在启用only
选项的情况下运行测试¥
--test-only
specifies to run tests with theonly
option enabled--node-arg=--inspect-brk
将启动节点调试器¥
--node-arg=--inspect-brk
will launch the node debugger
在 VS Code 中,创建并启动
Node.js: Attach
调试配置。无需修改。¥In VS Code, create and launch a
Node.js: Attach
debug configuration. No modification should be necessary.
现在你应该能够在代码编辑器中逐步执行测试文件(以及 Fastify
的其余部分)。
¥Now you should be able to step through your test file (and the rest of
Fastify
) in your code editor.
插件
¥Plugins
让我们进入一个名为 'testing-plugin-example' 的新目录,然后在终端中输入 npm init -y
。
¥Let's cd
into a fresh directory called 'testing-plugin-example' and type npm init
-y
in our terminal.
运行 npm i fastify fastify-plugin
¥Run 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)
插件的基本示例。参见 插件指南
¥A basic example of a Plugin. See Plugin Guide
test/myFirstPlugin.test.js:
const Fastify = require("fastify");
const { test } = require("node:test");
const myPlugin = require("../plugin/myFirstPlugin");
test("Test the Plugin Route", async t => {
// Create a mock fastify application to test the plugin
const fastify = Fastify()
fastify.register(myPlugin)
// Add an endpoint of your choice
fastify.get("/", async (request, reply) => {
return ({ message: request.helloRequest })
})
// Use fastify.inject to fake a HTTP Request
const fastifyResponse = await fastify.inject({
method: "GET",
url: "/"
})
console.log('status code: ', fastifyResponse.statusCode)
console.log('body: ', fastifyResponse.body)
})
了解有关 fastify.inject()
的更多信息。在你的终端 node test/myFirstPlugin.test.js
中运行测试文件
¥Learn more about fastify.inject()
.
Run the test file in your terminal node test/myFirstPlugin.test.js
status code: 200
body: {"message":"Hello World"}
现在我们可以用实际测试替换我们的 console.log
调用!
¥Now we can replace our console.log
calls with actual tests!
在你的 package.json
中,将 "test" 脚本更改为:
¥In your package.json
change the "test" script to:
"test": "node --test --watch"
为端点创建测试。
¥Create the test for the endpoint.
test/myFirstPlugin.test.js:
const Fastify = require("fastify");
const { test } = require("node:test");
const myPlugin = require("../plugin/myFirstPlugin");
test("Test the Plugin Route", async t => {
// Specifies the number of test
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
并查看你的测试结果!
¥Finally, run npm test
in the terminal and see your test results!
测试 .decorate()
和 .decorateRequest()
。
¥Test the .decorate()
and .decorateRequest()
.
test/myFirstPlugin.test.js:
const Fastify = require("fastify");
const { test }= require("node:test");
const myPlugin = require("../plugin/myFirstPlugin");
test("Test the Plugin Route", async t => {
t.plan(5)
const fastify = Fastify()
fastify.register(myPlugin)
fastify.get("/", async (request, reply) => {
// Testing the fastify decorators
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" })
})