Quantcast
Channel: CNode:Node.js专业中文社区
Viewing all articles
Browse latest Browse all 14821

eggjs 通过单元测试为API自动生成文档

$
0
0

这是原贴地址: https://cnodejs.org/topic/586e1810df04f6ab76081dc5

最近在用eggjs写后台,也想发布api之类的文档。以前看到过通过单元测试改造生成API文档,自己还是比较认可这个理念的,于是将其拿过来,改造了一下,适用于eggjs。

首先看看现在单测的写法:

test\controller\my.test.js

'use strict';
const test = require('../../../app/common/test');

describe('test/app/controller/home.test.js', () => {
  before(test.before);

  afterEach(test.afterEach);

  it('should GET doc', () => {
    test({
      file: 'user',
      group: '用户相关API',
      title: '获取用户信息',
      method: 'get',
      url: '/user/:id',
    })
    .get('/', {
        id: { value: '123abc', type: 'String', required: true, desc: '' },
    })
    .expect('hi, egg')
    .expect(200)
  });
});

单测代码书写方式没有太大变化,只是为了方便,做了一点点包装。

app\common\test.js

'use strict';
const mm = require('egg-mock');
const assert = require('assert');

let app;
let dir;

if(!global.docTimeout) {
    after( ()=>{
        fs.writeFileSync(path.resolve(dir, './docs/index.js'), JSON.stringify(global.docs, null, 4));
        mdRender(global.docs)
        console.log('文档生成完毕')
    } )
    global.docTimeout = true
}

module.exports = (opt) => {
  class Request {
      
    constructor(opt) {
      this.req = app.httpRequest();
      this.opt = opt;
    }

    request(type, url, params) {
      return this.req[type](url).send(params).expect(200, (err, res) => {
          if(!global.docs) global.docs = []
          this.opt.params = params
          this.opt.res = res.text
          global.docs.push(this.opt)
      });
    }

    get(url, params) {
      return this.request('get', url, params);
    }

    post(url, params) {
      return this.request('post', url, params);
    }
  }

  return  new Request(opt)
};

module.exports.before = () => {
  app = mm.app();
  //记录根目录
  if(!dir){
      dir = app.config.baseDir
  }
  return app.ready();
};

module.exports.afterEach = mm.restore;

// markdown 渲染
const fs = require('fs');
const path = require('path');
function mdRender(docs){
    const mdStr = {};
    docs.forEach((obj) => {
        if (!mdStr[obj.group]) {
            mdStr[obj.group] = '';
            mdStr[obj.group] += '## ' + obj.group + '\n\n';
        }
        const fields = {};
        mdStr[obj.group] += `### ${ obj.title } \`${ obj.method }\` ${ obj.url } \n\n#### 参数\n`;
        mdStr[obj.group] += '\n参数名 | 类型 | 是否必填 | 说明\n-----|-----|-----|-----\n';
        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'] } \n`;
        });
        mdStr[obj.group] += '\n#### 使用示例\n\n请求参数: \n\n';
        mdStr[obj.group] += '```json\n' + JSON.stringify(fields, null, 2) + '\n```\n';
        mdStr[obj.group] += '\n返回结果:\n\n';
        if (obj.url.indexOf(':') > -1) {
            obj.url = obj.url.replace(/:\w*/g, function (word) {
            return fields[word.substr(1)];
            });
        }
        mdStr[obj.group] += '```json\n' + JSON.stringify(obj.res, null, 2) + '\n```\n';
        mdStr[obj.group] += '\n';
        fs.writeFileSync(path.resolve(dir, './docs/', obj.file + '.md'), mdStr[obj.group]);
    })
}

代码编写仓促,难免有疏漏之处。

起初另外由于不知道怎么在所有单测执行完毕后触发事件,开始尝试在process上监听exit事件,输出最后的文档。后来去eggjs提了一个issue,才知道可以直接用after。

感觉eggjs的团队,问题回答总是这样高效、快速。


Viewing all articles
Browse latest Browse all 14821

Trending Articles