yuheng/doc/fastify-docs/docs/Testing.md
2025-03-19 15:54:28 +08:00

329 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<h1 align="center">Fastify</h1>
## 测试
测试是开发应用最重要的一部分。Fastify 处理测试非常灵活并且它兼容绝大多数框架 (例如 [Tap](https://www.npmjs.com/package/tap)。下面的例子都会用这个演示)。
让我们 `cd` 进入一个全新的 'testing-example' 文件夹,并在终端里输入 `npm init -y`
执行 `npm install fastify && npm install tap pino-pretty --save-dev`
### 关注点分离让测试变得轻松
首先,我们将应用代码与服务器代码分离:
**app.js**:
```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**:
```js
"use strict";
const server = require("./app")({
logger: {
level: "info",
prettyPrint: true,
},
});
server.listen(3000, (err, address) => {
if (err) {
console.log(err);
process.exit(1);
}
});
```
### 使用 fastify.inject() 的好处
感谢有 [`light-my-request`](https://github.com/fastify/light-my-request)Fastify 自带了伪造的 HTTP 注入。
在进行任何测试之前,我们通过 `.inject` 方法向路由发送假的请求:
**app.test.js**:
```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。
`.inject` 确保了所有注册的插件都已引导完毕,可以开始测试应用了。之后请求方法将被传递到路由函数中去。使用 await 可以存储响应,且避免了回调函数。
在终端执行 `node app.test.js` 来开始测试。
```sh
status code: 200
body: {"hello":"world"}
```
### HTTP 注入测试
现在我们能用真实的测试语句代替 `console.log` 了!
`package.json` 里修改 "test" script 如下:
`"test": "tap --reporter=list --watch"`
**app.test.js**:
```js
"use strict";
const { test } = require("tap");
const build = require("./app");
test('requests the "/" route', async (t) => {
const app = build();
const response = await app.inject({
method: "GET",
url: "/",
});
t.equal(response.statusCode, 200, "returns a status code of 200");
});
```
执行 `npm test`,查看结果!
`inject` 方法能完成的不只有简单的 GET 请求:
```js
fastify.inject(
{
method: String,
url: String,
query: Object,
payload: Object,
headers: Object,
cookies: Object,
},
(error, response) => {
// 你的测试
},
);
```
忽略回调函数,可以链式调用 `.inject` 提供的方法:
```js
fastify
.inject()
.get("/")
.headers({ foo: "bar" })
.query({ foo: "bar" })
.end((err, res) => {
// 调用 .end 触发请求
console.log(res.payload);
});
```
或是用 promise 的版本
```js
fastify
.inject({
method: String,
url: String,
query: Object,
payload: Object,
headers: Object,
cookies: Object,
})
.then((response) => {
// 你的测试
})
.catch((err) => {
// 处理错误
});
```
Async await 也是支持的!
```js
try {
const res = await fastify.inject({
method: String,
url: String,
payload: Object,
headers: Object,
});
// 你的测试
} catch (err) {
// 处理错误
}
```
#### 另一个例子:
**app.js**
```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**
```js
const tap = require("tap");
const buildFastify = require("./app");
tap.test("GET `/` route", (t) => {
t.plan(4);
const fastify = buildFastify();
// 在测试的最后,我们强烈建议你调用 `.close()`
// 方法来确保所有与外部服务的连接被关闭。
t.teardown(() => fastify.close());
fastify.inject(
{
method: "GET",
url: "/",
},
(err, response) => {
t.error(err);
t.equal(response.statusCode, 200);
t.equal(
response.headers["content-type"],
"application/json; charset=utf-8",
);
t.same(response.json(), { hello: "world" });
},
);
});
```
### 测试正在运行的服务器
你还可以在 fastify.listen() 启动服务器之后,或是 fastify.ready() 初始化路由与插件之后,进行 Fastify 的测试。
#### 举例:
使用之前例子的 **app.js**
**test-listen.js** (用 [`Request`](https://www.npmjs.com/package/request) 测试)
```js
const tap = require("tap");
const request = require("request");
const buildFastify = require("./app");
tap.test("GET `/` route", (t) => {
t.plan(5);
const fastify = buildFastify();
t.teardown(() => fastify.close());
fastify.listen(0, (err) => {
t.error(err);
request(
{
method: "GET",
url: "http://localhost:" + fastify.server.address().port,
},
(err, response, body) => {
t.error(err);
t.equal(response.statusCode, 200);
t.equal(
response.headers["content-type"],
"application/json; charset=utf-8",
);
t.same(JSON.parse(body), { hello: "world" });
},
);
});
});
```
**test-ready.js** (用 [`SuperTest`](https://www.npmjs.com/package/supertest) 测试)
```js
const tap = require("tap");
const supertest = require("supertest");
const buildFastify = require("./app");
tap.test("GET `/` route", async (t) => {
const fastify = buildFastify();
t.teardown(() => fastify.close());
await fastify.ready();
const response = await supertest(fastify.server)
.get("/")
.expect(200)
.expect("Content-Type", "application/json; charset=utf-8");
t.same(response.body, { hello: "world" });
});
```
### 如何检测 tap 的测试
1. 设置 `{only: true}` 选项,将需要检测的测试与其他测试分离
```javascript
test('should ...', {only: true}, t => ...)
```
2. 通过 `npx` 运行 `tap`
```bash
> npx tap -O -T --node-arg=--inspect-brk test/<test-file.test.js>
```
- `-O` 表示开启 `only` 选项,只运行设置了 `{only: true}` 的测试
- `-T` 表示不设置超时
- `--node-arg=--inspect-brk` 会启动 node 调试工具
3. 在 VS Code 中创建并运行一个 `Node.js: Attach` 调试配置,不需要额外修改。
现在你便可以在编辑器中检测你的测试文件 (以及 `Fastify` 的其他部分) 了。