如何根据protobuf来Mock后台返回的数据
Protobuf
Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准,他们用于 RPC 系统和持续数据存储系统。
Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。为什么要讲到它,因为我们后台的协议就是用的它。
Mock
Mock也可以叫mock object,模拟对象,在面向对象程序设计中,以可控的方式模拟真实对象行为的假对象。前端比较有名的库是Mock.js。
为什么要mock呢?
1、前后端分离,并行开发,能够充分利用人力,避免等待。
2、可以丰富测试用例,提前模拟很多的真实场景数据,而不必等到线上环境才发现问题。
但Mock.js有个问题:学习和配置成本高。
// 配置 Mock 路径 require.config({ paths: { mock: 'http://mockjs.com/dist/mock' } }) // 加载 Mock require(['mock'], function(Mock){ // 使用 Mock var data = Mock.mock({ 'list|1-10': [{ 'id|+1': 1 }] }) // 输出结果 document.body.innerHTML += '<pre>' + JSON.stringify(data, null, 4) + '</pre>' }) // ==> { "list": [ { "id": 1 }, { "id": 2 }, { "id": 3 } ] }
用它之前必须要先配置一个Ajax的请求路径,然后根据后台的协议,学习mock的语法,才能模拟出list这个数组。所以这里存在着两个问题:
1、配置成本和语法学习成本
2、后台协议转换为mock语法
很多时候,等到我们开发完业务逻辑,已经没有多少耐心来写这个mock逻辑,简单的协议还好,如果涉及到很复杂的,写这个mock的逻辑就更加不愿意。
为了帮前端同学偷个懒,有必要把这个过程给省了。
Protobuf转换为JSON
git上已经有针对Protobuf开源的js接口:https://github.com/dcodeIO/pr...。因为构建工具我们使用的是gulp,同样的也有个对应的插件:gulp-protobufjs,但是没法拿来就用。
首页我们来看下这个gulp-protobufjs插件所做的事情
var gulp = require('gulp'); var gulpprotobuf = require('gulp-protobufjs'); gulp.task('default', function () { return gulp.src('file.proto') .pipe(gulpprotobuf()) .pipe(gulp.dest('out/')); });
读取proto文件,然后生成一个文件,文件类型可以自定义。
如果产出的是.js文件,那么结果是commonjs模块文件:
module.exports = require("protobufjs").newBuilder({})['import']({ "package": "mmgameweappwap", "syntax": "proto2", "messages": [ { "name": "User", "syntax": "proto2", "fields": [ { ……
如果是.json文件:
{ "package": "mmgameweappwap", "syntax": "proto2", "messages": [ { "name": "UserItem", "syntax": "proto2", "fields": [ { "rule": "required", "type": "string", "name": "user_id", "id": 1 }, { "rule": "optional", "type": "string", "name": "head_img_url", "id": 2 }, { "rule": "optional", "type": "string", "name": "nick_name", "id": 3 } ] }, ……
很显然,这不是我们想要的最终结果。我们想要的结果是最终的ajax返回的数据。当然有了协议的json结构,字段类型、字段名也都有了,剩下的事情就只需根据他们mock一些数据。这个json文件可以理解为对接口的一个描述文件。
pb协议的rule常见的有以下几种:
optional:可选
required:必须
repeated:数组
数据类型,有以下几种最基本的类型,基本都可以映射为js常用的数据类型。
例子
在解析pb文件的时候,后台CGI接口名会被定义为message。
package mmgameweappwap; message GetDiceGameRoomRequest { required string room_id = 1; } message GetDiceGameRoomResponse { required int32 errcode = 1; required string errmsg = 2; optional OkResult data = 3; message OkResult { repeated UserDiceItem user_dice_list = 1; required uint32 room_close_remain_second = 2; required bool room_closed = 3; } }
package 可以理解为模块,上文我们定义了一个mmgameweappwap模块。
GetDiceGameRoomRequest:请求的message
GetDiceGameRoomResponse:返回的message
GetDiceGameRoom:接口名称
一个CGI接口在pb协议中的定义一般会成对的出现。Request
、Response
是所有接口标准的后缀,以区别普通的message。依赖这种规则,就可以把接口解析出来,并且也能够知道其请求、返回的message。
从上面的pb协议看,message可以被嵌套,OkResult是属于GetDiceGameRoomResponse子message。同一个pb文件下,message名可能重复,整个协议可以看成是一颗树。如果要找对应的message,必须从当前节点的子节点开始查找,否则可能找错。
处理的流程比较简单。如下图所示:
通过分析知道接口名后,如果前端的请求都是统一的,就可以通过请求接口的代码模板生成请求代码。生成对应的API.js:
var Promise = require('js/libs/Promise'); var Util = require('js/common/util'); var Mock = require('./mock'); var app = getApp(); var FETCH_URL = 'http://xxxxx/gameweappwap/'; module.exports = { getDiceGameRoom: function(data) { return new Promise((resolve, reject) => { if(app.mock){ resolve(Mock.getDiceGameRoom); return; } app.request({ url: FETCH_URL + 'getsmobapremadeinfo', data: { room_id: data.room_id, // string }, method: "POST", header: { 'content-type': 'application/json' }, success: resolve, fail: reject, ……
exports对外的接口就是CGI的接口名:getDiceGameRoom。
request中的roomid就是从pb协议中解析出来的,后面的注释标注了其类型。这样一个ajax请求就非常的简单明了了。开发者只需要API.getDiceGameRoom调用即可。如果当前app.mock开发开启,就回直接返回mock数据。mock数据是与API.js同级的文件。
这里的数据都是调用mock.js生成的,当然我们可以加入一些业务的数据,这样测试起来更加真实。
运用场景
1、前端安全,模拟xss例子。
随机生成一些xss用例,用于前端页面的测试。
2、前端UI测试。
在实际工作中,经常会遇到文本截断的问题。UI界面在多行文本的情况下会不会表现正常这个问题,在这种情况下就可以轻易的测试出来,而不需每次叫后台开发配合加一些假数据。
如下图,我们可以针对字符串类型的数据生成一些长文本。
结语
所有的项目都在统一框架下,每个项目都保持统一的目录结构,只需要自定义一些配置就能初始化项目脚手架,剩下的只需要专注于业务开发。
by addy 原创文章,欢迎转载,但希望全文转载,注明本文地址。http://www.iamaddy.net/2017/0...