在Node.js中通过单元测试自动为API生成文档编写符合规范的相应注释,然后运行相应的命令生成静态网页形式的文档。使用注解生成文档的好处是,无论是常用的功能还是API,只要写上相应的注解,就可以生成相应的文档。不过这种方式总感觉有点繁琐,尤其是只需要为API生成文档的时候。手动编码大量输入和输出作为使用示例。而我只是想要markdown格式的文档,丢到内部的Gitlabwiki上供前端人员查看,然后就可以根据commithistory查看不同的版本了。如果你不想为API文档手动写一大堆输入输出,那哪里会有输入输出呢?人们很容易认为单元测试会产生输入和输出。好的,所以使用单元测试来记录您的API。我在单元测试中主要使用的库是mocha、supertest和power-assert,web框架是express。完整代码示例://app.jsconstexpress=require('express');constbodyParser=require('body-parser');constapp=express();app.use(bodyParser.json());app.use(bodyParser.urlencoded({extended:true}));app.post('/user/create',function(req,res){constname=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){constname=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){constuserId=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(`服务器正在监听3000`);});module.exports=应用程序;下面是测试文件//test/uset.test.jsconstassert=require('power-assert');constsupertest=require('supertest');constU=require('../utils');constapp=require('../app');constagent=supertest.agent(app);describe('测试',function(){it('应该创建用户成功',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:{entry:'client'},expect:200,callback(err,res){if(err)returndone(err);断言(res.body.data.name==='node');assert(res.body.data.age===23);done();},});});it('shouldsearchusersuccess',function(done){U.test({agent,file:'user',group:'user-relatedAPI',title:'searchuser',method:'get',url:'/user/search'params:{name:{value:'node',type:'String',required:true,desc:'name'},},expect:200,callback(err,res){if(err)returndone(err);}assert(res.body.data.name==='node');断言(res.body.data.age===23);完毕();},});});it('应该搜索用户成功',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)returndone(err);assert(res.body.data.name==='node');assert(res.body.data.age===23);done();},});});});在测试文件中,测试用例的代码调用了utils.js中的test方法,该方法的主要作用是接收单元测试的输入输出并生成对于对应的文档,需要传递一个对象作为参数给测试方法,对象中的字段解释如下:agent:调用API的代理file:生成文档的文件名。group:一组文档的名称。title:界面名称。method:接口的方法。params:接口的参数,即input。headers:添加到请求标头中的信息。expect:超测的期望值。callback:超测方法结束的回调函数。utils.js代码如下://utils.jsconstpath=require('path');constfs=require('fs');常量mdStr={};exports.test=function(obj){if(!mdStr[obj.group]){mdStr[obj.group]='';mdStr[obj.group]+='##'+obj.group+'\n\n';}常量字段={};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){constparamVal=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){返回字段[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\n'+JSON.stringify(res.body,null,2)+'\n```\n';mdStr[obj.group]+='\n';if(process.env['GEN_DOC']>0){fs.writeFileSync(path.resolve(__dirname,'./docs/',obj.file+'.md'),mdStr[obj.group]);}obj.callback(err,res);});}这样,在根目录下创建一个docs目录,运行npmruntest:doc命令,就会在docs目录下生成文档.如果不想在运行单元测试的时候生成文档,可以直接使用npmtest。对应的package.json配置如下:"scripts":{"test":"exportNODE_ENV='test'&&mocha","test:doc":"exportNODE_ENV='test'&&exportGEN_DOC=1&&mocha"}如果你不想为某个API生成文档,就不要调用utils的测试,按照原来的写法即可。如果需要对参数进行签名,可以在调用测试方法时添加sign:true这样的配置,然后在测试方法中进行相应的判断并实现相应的签名。生成文档内容如下:原文地址:通过单元测试为API自动生成文档
