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

6.6 KiB
Raw Blame History

Fastify

测试

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

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

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

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

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

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",
    prettyPrint: true,
  },
});

server.listen(3000, (err, address) => {
  if (err) {
    console.log(err);
    process.exit(1);
  }
});

使用 fastify.inject() 的好处

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

在进行任何测试之前,我们通过 .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("status code: ", response.statusCode);
  console.log("body: ", response.body);
};
test();

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

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

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

status code:  200
body:  {"hello":"world"}

HTTP 注入测试

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

package.json 里修改 "test" script 如下:

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

app.test.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 请求:

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) => {
    // 处理错误
  });

Async await 也是支持的!

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 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 测试)

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 测试)

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} 选项,将需要检测的测试与其他测试分离
test('should ...', {only: true}, t => ...)
  1. 通过 npx 运行 tap
> 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 的其他部分) 了。