资讯专栏INFORMATION COLUMN

在Node.js中通过单元测试为API自动生成文档

smallStone / 1802人阅读

摘要:我在单元测试中主要用的库有和,框架为。如果运行单元测试不想生成文档,直接用就可以了,相应的配置如下如果不想为某个生成文档,就不要调用的,直接按原生的写法就可以了。生成的文档内容形式如下原文地址通过单元测试为自动生成文档

在开发中,为项目生成文档是很常见的需求,很多第三方库(如jsdoc、swagger等)的做法是为需要生成文档的函数编写相应的符合规范的注释,然后运行相应的命令,生成一个静态网页形式的文档。

用注释生成文档的好处是可以为无论是普通函数还是 API,只要编写了相应的注释都能生成相应的文档,然而这种做法总觉得有点繁琐,尤其是只需要为 API 生成文档的时候,需要手动编写大量的输入和输出作为使用示例。而且我只想需要 markdown 形式的文档,丢在内部 Gitlab 的 wiki 上供前端人员査阅,然后可以根据 commit 的 history 查阅不同的版本。

不想手动为 API 文档编写大量的输入输出,那哪里会有输入输出呢,就很容易的想到了单元测试会产生输入和输出。好,那就用单元测试来为 API 生成文档。

我在单元测试中主要用的库有 mocha、supertest和power-assert,Web 框架为 express。

完整代码示例:

// app.js
const express = require("express");
const bodyParser = require("body-parser");
const app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

app.post("/user/create", function (req, res) {
  const name = req.body.name;
  res.json({
    status: 200,
    error_code: 0,
    message: "success",
    data: {
      id: "123abc",
      name: "node",
      gender: "male",
      age: 23,
    },
  });
});

app.get("/user/search", function (req, res) {
  const name = req.query.name;
  res.json({
    status: 200,
    error_code: 0,
    message: "success",
    data: {
      id: "123abc",
      name: "node",
      gender: "male",
      age: 23,
    },
  });
});

app.get("/user/:id", function (req, res) {
  const userId = req.params.id;
  res.json({
    status: 200,
    error_code: 0,
    message: "success",
    data: {
      id: "123abc",
      name: "node",
      gender: "male",
      age: 23,
    },
  });
});

app.listen(3000, function () {
  console.log(`Server is listening on 3000`);
});
module.exports = app;

下面是测试文件。

// test/uset.test.js
const assert = require("power-assert");
const supertest = require("supertest");
const U = require("../utils");
const app = require("../app");
const agent = supertest.agent(app);

describe("Test", function () {
  it("should create user success", function (done) {
    U.test({
      agent,
      file: "user",
      group: "用户相关API",
      title: "创建用户",
      method: "post",
      url: "/user/create"
      params: {
        name: { value: "node", type: "String", required: true, desc: "名称" },
        gender: { value: "male", type: "String", required: false, desc: "性别" },
        age: { value: 23, type: "Int", required: false, desc: "" },
      },
      headers: { entrance: "client" },
      expect: 200,
      callback (err, res) {
        if (err) return done(err);

        assert(res.body.data.name === "node");
        assert(res.body.data.age === 23);
        done();
      },
    });
  });

  it("should search user success", function (done) {
    U.test({
      agent,
      file: "user",
      group: "用户相关API",
      title: "搜索用户",
      method: "get",
      url: "/user/search"
      params: {
        name: { value: "node", type: "String", required: true, desc: "名称" },
      },
      expect: 200,
      callback (err, res) {
        if (err) return done(err);

        assert(res.body.data.name === "node");
        assert(res.body.data.age === 23);
        done();
      },
    });
  });

  it("should search user success", function (done) {
    U.test({
      agent,
      file: "user",
      group: "用户相关API",
      title: "获取用户信息",
      method: "get",
      url: "/user/:id"
      params: {
        id: { value: "123abc", type: "String", required: true, desc: "" },
      },
      expect: 200,
      callback (err, res) {
        if (err) return done(err);

        assert(res.body.data.name === "node");
        assert(res.body.data.age === 23);
        done();
      },
    });
  });
});

在测试文件中,测试用例的代码调用到了 utils.js 中的 test 方法,该方法的主要作用是接收单元测试的输入和输出并生成相应的文档,其中需要向 test 方法传入一个对象作为参数,对象中的字段解读如下:

agent:调用 API 的代理。
file:生成的文档的文件名称。
group:某一组文档的名称。
title:接口的名称。
method:接口的方法。
params:接口的参数,即输入。
headers: 添加到请求头中的信息。
expect:supertest 的expect。
callback:supertest 方法的 end 的回调函数。

utils.js代码如下:

// utils.js
const path = require("path");
const fs = require("fs");

const mdStr = {};
exports.test = function (obj) {
  if (!mdStr[obj.group]) {
    mdStr[obj.group] = "";
    mdStr[obj.group] += "## " + obj.group + "

";
  }
  const fields = {};

  mdStr[obj.group] += `### ${ obj.title } `${ obj.method }` ${ obj.url } 

#### 参数
`;
  mdStr[obj.group] += "
参数名 | 类型 | 是否必填 | 说明
-----|-----|-----|-----
";
  Object.keys(obj.params).forEach(function (param) {
    const paramVal = obj.params[param];
    fields[param] = paramVal["value"];
    mdStr[obj.group] += `${ param } | ${ paramVal["type"] } | ${ paramVal["required"] ? "是" : "否" } | ${ paramVal["desc"] } 
`;
  });
  mdStr[obj.group] += "
#### 使用示例

请求参数: 

";

  mdStr[obj.group] += "```json
" + JSON.stringify(fields, null, 2) + "
```
";
  mdStr[obj.group] += "
返回结果:

";

  if (obj.url.indexOf(":") > -1) {
    obj.url = obj.url.replace(/:w*/g, function (word) {
      return fields[word.substr(1)];
    });
  }

  obj.agent[obj.method](obj.url)
  .set(obj.header || {})
  .query(fields)
  .send(fields)
  .expect(obj.expect)
  .end(function (err, res) {
    mdStr[obj.group] += "```json
" + JSON.stringify(res.body, null, 2) + "
```
";
    mdStr[obj.group] += "
";

    if (process.env["GEN_DOC"] > 0) {
      fs.writeFileSync(path.resolve(__dirname, "./docs/", obj.file + ".md"), mdStr[obj.group]);
    }
    obj.callback(err, res);
  });
}

这样,在根目录创建一个 docs 目录,运行 npm run test:doc 命令,就会在 docs 目录下生成文档。如果运行单元测试不想生成文档,直接用npm test就可以了,相应的package.json配置如下:

"scripts": {
  "test": "export NODE_ENV="test" && mocha",
  "test:doc": "export NODE_ENV="test" && export GEN_DOC=1 && mocha"
}

如果不想为某个 API 生成文档,就不要调用 utils 的 test,直接按原生的写法就可以了。

若需要对参数进行签名,可在调用 test 方法时,增加形如sign: true的配置,然后在 test 方法中做相应的判断和实现相应的签名。

生成的文档内容形式如下:

原文地址:通过单元测试为API自动生成文档

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/88071.html

相关文章

  • 使用prince-cli,轻松构建高性能React SPA项目~

    摘要:对模块进行了打包,监听文件更改刷新等功能,创建了个服务,分别为静态资源服务用于代理本地资源,与自刷新浏览器请求服务用于接受,请求,返回数据服务用于收发消息。除了项目,还可以换成项目。项目地址如果觉得对你有所帮助,多谢支持 prince-cli 快速指南 这是一个为快速创建SPA所设计的脚手架,旨在为开发人员提供简单规范的开发方式、服务端环境、与接近native应用的体验。使用它你能够获...

    roundstones 评论0 收藏0
  • 【译】测试驱动开发:使用 Node.js 和 MongoDB 构建 Todo API

    摘要:首先安装单元测试环境使用模块来模拟定义的模型。根据删除这是单元测试的最后一小节。需要根据需求和单元测试用例来编写应用逻辑,使我们的程序更加稳定。我们会运行自动测试用例,一直重构,直到所有单元测试都通过。 本文转载自:众成翻译译者:文蔺链接:http://www.zcfy.cc/article/746原文:https://semaphoreci.com/community/tutoria...

    邱勇 评论0 收藏0
  • 【译】测试驱动开发:使用 Node.js 和 MongoDB 构建 Todo API

    摘要:首先安装单元测试环境使用模块来模拟定义的模型。根据删除这是单元测试的最后一小节。需要根据需求和单元测试用例来编写应用逻辑,使我们的程序更加稳定。我们会运行自动测试用例,一直重构,直到所有单元测试都通过。 本文转载自:众成翻译译者:文蔺链接:http://www.zcfy.cc/article/746原文:https://semaphoreci.com/community/tutoria...

    tomener 评论0 收藏0
  • mocha+power-assert+istanbul测试库基础用法

    摘要:块就是测试用例,表示一个单独的测试,块可以包含多个断言块块就是断言,判断预期结果和执行结果是否一致失败的话会打印错误提示注意因为箭头函数不能够访问的上下文,所以在需要用到上下文的时候不能使用,否则报错。 前言 我的英文水平有点渣,所以就贴上原文给你们自己翻译了,还有github地址,如果发现有些中文文档的话也会顺便赠送飞机票 mocha Mocha is a feature-rich ...

    ingood 评论0 收藏0
  • Nodejs中贯彻单元测试

    摘要:原文链接在中贯彻单元测试在团队合作中,你写好了一个函数,供队友使用,跑去跟你的队友说,你传个值进去,他就会返回结果了。如果你也为社区贡献过,想更多人使用的话,加上单元测试吧,让你的值得别人信赖。 原文链接:BlueSun | 在Nodejs中贯彻单元测试 在团队合作中,你写好了一个函数,供队友使用,跑去跟你的队友说,你传个A值进去,他就会返回B结果了。过了一会,你队友跑过来说,我传个A...

    enali 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<