by addy 原创文章,欢迎转载,但希望全文转载,注明本文地址。

本文地址:http://www.iamaddy.net/2017/08/mock-data-from-protobuf/

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/protobuf.js。因为构建工具我们使用的是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常用的数据类型。

根据protobuf自动Mock数据的正确姿势

例子

在解析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协议中的定义一般会成对的出现。RequestResponse是所有接口标准的后缀,以区别普通的message。依赖这种规则,就可以把接口解析出来,并且也能够知道其请求、返回的message。

从上面的pb协议看,message可以被嵌套,OkResult是属于GetDiceGameRoomResponse子message。同一个pb文件下,message名可能重复,整个协议可以看成是一颗树。如果要找对应的message,必须从当前节点的子节点开始查找,否则可能找错。

处理的流程比较简单。如下图所示:
根据protobuf自动Mock数据的正确姿势
通过分析知道接口名后,如果前端的请求都是统一的,就可以通过请求接口的代码模板生成请求代码。生成对应的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同级的文件。

根据protobuf自动Mock数据的正确姿势

这里的数据都是调用mock.js生成的,当然我们可以加入一些业务的数据,这样测试起来更加真实。

运用场景

1、前端安全,模拟xss例子。
根据protobuf自动Mock数据的正确姿势

随机生成一些xss用例,用于前端页面的测试。

2、前端UI测试。

在实际工作中,经常会遇到文本截断的问题。UI界面在多行文本的情况下会不会表现正常这个问题,在这种情况下就可以轻易的测试出来,而不需每次叫后台开发配合加一些假数据。

如下图,我们可以针对字符串类型的数据生成一些长文本。
根据protobuf自动Mock数据的正确姿势

结语

所有的项目都在统一框架下,每个项目都保持统一的目录结构,只需要自定义一些配置就能初始化项目脚手架,剩下的只需要专注于业务开发。
根据protobuf自动Mock数据的正确姿势

本文为原创文章,可能会经常更新知识点以及修正一些错误,因此转载请保留原出处,方便溯源,谢谢合作

本文地址:http://www.iamaddy.net/2017/08/mock-data-from-protobuf/

想要打赏?你的鼓励是我前进的动力! addy打赏二维码

关注个人公众号web_lab,不定期更新一些干货~ web_lab公众号