在开发中,为项目生成文档是很常见的需求,很多第三方库(如 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();
},
});
});
});