1.3 使用Mock.js

Mock.js[1]是使用JavaScript语言生成测试数据的第三方库,它能用一套相对规范的模板语法让开发者自行定制测试数据的结构,然后根据模板生成用于测试的数据。Mock.js不仅可以使用类似于“@date”“@cname”这样的模板占位符来生成时间和中文姓名等特殊的测试数据,而且可以编写自定义的扩展占位符来生成需要的测试数据,更方便的是,仅仅使用简单的标记符就可以生成大量结构重复的测试数据。如果你使用过任何SPA(Single Page Application,单页面应用)框架,就不难联想到Mock.js与“*ngFor”“v-for”这类用于生成重复结构的模板语法如出一辙。

1.3.1 Mock.js的语法规范

Mock.js的语法规范可以分为数据模板定义规范和数据占位符定义规范,规范的详细内容可以在项目GitHub仓库的wiki页中查看和学习。

1. 数据模板定义规范

数据模板中的每个属性均由3部分构成:属性名、生成规则和属性值。数据模板的格式如下:

"<name>|<rule>": <value>

这个简单的规则会依据value的类型将rule解释为不同的编译规则。例如,当value的类型为number时,rule的不同写法可以最终生成指定范围内的整数,或者指定范围和小数位数的浮点数;而当value的类型为Array时,rule的不同写法可以最终生成数组中的某一项,或者是将value重复若干次后得到的新数组。或许这些规则最初会让人感到很烦琐,但实际上在手动模拟2~3个接口的响应之后,绝大多数开发人员很容易掌握其中的规律。

2. 数据占位符定义规范

占位符是写在属性值字符串中的,在最终生成数据时,会依据定义替换为对应的内容。数据占位符的格式如下:

'name|rule': '@占位符'
'name|rule': '@占位符(参数[,参数])'

Mock.js内置的占位符可以生成常见的字符串,诸如姓名、时间、地址、段落、指定大小的图片链接、符合某个正则表达式的字符串等,只需要在数据模板的value字段处使用“@[keywords]”的形式进行声明即可。同时,Mock.Random命名空间下也提供了与占位符同名的生成函数,开发人员可以根据自己的需求编写新的占位符。

拓展知识

将同类别的方法集中到命名空间下是一种非常好的编程习惯,因为这样做可以避免全局环境的污染。浏览器脚本的全局环境是一个无限制的命名空间,你挂载在全局环境中的方法有可能会覆盖其他人的同名方法,也有可能被后来的开发人员所编写的函数覆盖。可怕的是,这样的覆盖并不是语法错误,所以你的程序有可能并没有报错,而最终的业务逻辑却出现了与预期不符的结果,这种情况下的调试往往会非常困难。初级开发人员应该从最初学习编程时就培养良好的习惯,独立的命名空间意味着如果你的代码写得很好,那么其他开发人员可以直接引入以进行复用;如果你的代码写得不够好,或者最终由于业务逻辑变更而失效,那么你也可以很轻松地用新的函数替代它。

1.3.2 Mock.js实战

Mock.js支持在浏览器环境和Node.js环境中使用。在浏览器中,Mock.js既可以使用<script>标签直接引入,也可以使用诸如require.js等符合AMD规范的包管理库进行加载;在服务端Node.js环境中,通过“npm install mockjs”命令安装依赖项并引入该模块即可。引入Mock.js后,就可以在本地Web服务中监听对应的请求并返回虚拟数据了。起初,前端开发人员可能需要对照后端开发人员提供的接口文档来手动定制测试数据,尽管Mock.js可以方便地生成序列数据,但如果业务数据的结构本身就很复杂,那么手动构造数据依然是一件效率低下的事情。如果熟悉Node.js应用开发,可以尝试编写一些自动化工具来完成上面的工作,这样就可以根据后端接口文档中的定义直接自动生成Mock数据模板,从而提高工作效率。待接口数量逐渐增多,这样做的好处就会体现出来,自动化工具可以很方便地实现批量接口回归测试和健康度检查等工程任务。下面就通过两个实例来介绍Mock.js最常见的使用场景。

实例1:请求/get_userinfo接口时返回模拟的用户信息

Mock.mock('/get_userinfo','GET',{
    'status|1':true,                      //标识请求是否成功,返回true的概率是1/2
    'message':'@csentence',               //请求失败时返回错误信息,使用占位符返回中文句子
    'data':{                            
        'id|1-20':0,                      //id为1~20之间的整数,0表示返回值为数字类型
        'nickname' : '@ctitle',           //昵称使用中文标题占位符
        'realname' : '@cname',            //实名使用中文名称占位符
        'birthday' : '@date',             //生日使用日期占位符
        'signature' : '@csentence',       //签名使用中文语句占位符
        'address' : '@county(true)',      //城市占位符转译格式为'陕西省 西安市'
        'email' : '@email',               //邮箱使用邮箱占位符
        'openId' : '@word(28)',           //生成28位字符串模拟ID
        'avatar' : '@dataImage(200x100)', //生成尺寸为200×100的头像图片链接
        'account' : '2000-3000.2':1,      //账户余额整数部分为2000~3000,小数点后保留2位
    }
})

当程序向“/get_userinfo”接口发送GET请求时,会得到Mock.js利用上述模板编译出的结果,注意,每次返回的结果都是不同的,但是都符合模板的格式,示例如下:

{
    status:true,
    message: '是真总为开土子于放对又际因。',
    data:{
        id:8,
        nickname:'院争议',
        realname:'张静',
        birthday:'1092-02-18',
        signature:'话经做技展使放至又太色委可每',
        address:'陕西省 西安市',
        email:'cgqqwjb@uaqfy.ev',
        openId:'ahtisldienkdsdfwieurnslidnfg',
        avatar:'http://dummyimage.com/125x125',
        account:2048.12
    }
}

实例2:请求/get_orders接口返回模拟的订单列表

Mock.mock('/get_orders', 'GET',{
    'status|1':true,                                //标识请求是否成功,返回true的概率是1/2
    'message':'@csentence',                         //请求失败时返回错误信息,使用占位符返回中文句子
    'data|4':{                                      //将返回数组,数组包含4个具有相同格式的对象
        'id|+1':0,                                  //id为从0开始的自增整数
        'create_time':'@datetime',                  //创建日期使用日期时间占位符
        'request_price|2000-3000.2':0,              //应收金额整数部分为2000~3000,小数点后保留2位
        'pay_price|1000-2000':0,                    //实付金额为1000~2000之间的整数
        'status|1':['已支付','已发货','已收货']        //订单状态为数组中的某一项
    }
})

当程序向“/get_orders”接口发送GET请求时,会得到一个数组型的有效数据,其格式如下:

{
    status:true,
    message:'这里仅仅给出一定长度的文字。',
    data:[{
        id:0,
        create_time:'1990-08-12 12:00:23',
        request_price:2048.10,
        pay_price:1050,
        status:'已支付'
    },{
        id:1,
        create_time:'2010-09-12 14:00:50',
        request_price:2400.10,
        pay_price:1250,
        status:'已收货'
    },{
        id:2,
        create_time:'1998-01-01 12:00:23',
        request_price:2128.40,
        pay_price:1050,
        status:'已收货'
    },{
        id:3,
        create_time:'2018-08-12 20:00:23',
        request_price:2648.10,
        pay_price:1800,
        status:'已支付'
    }]
}

1.3.3 自定义扩展

Mock.js官方提供了大量基础且常用的占位符,但很多随机生成的数据都是无意义的,仅仅是为了在调整样式的时候更方便一些。在日常开发中,我们极有可能需要向客户或非技术人员进行阶段性的功能演示,一些贴近真实场景的虚拟数据能够有效地降低非技术人员的理解难度以及沟通成本。此时,我们就可以利用Mock.js提供的自定义扩展接口定制符合开发需求或业务逻辑的占位符。Mock.js中提供了Mock.Random.extend方法来扩展占位符,该方法作为占位符时,会从给定的枚举项中随机选取某一项来生成数据。下面通过一个示例来说明,我们通过添加一个占位符@car来获得一些静态的汽车信息,示例代码如下:

Mock.Random.extend({
    car: function(){
        var cars = ['大众', '别克', '劳斯莱斯', '保时捷', '迈巴赫', '公交车'];
        return this.pick(cars);
    }
});

添加上述代码后,我们就可以在模板中使用@car占位符获得一些车辆信息了。

拓展知识

阶段性的功能演示是开发过程中的常见环节,千万不要直接以开发中的版本向非技术人员做功能演示,对于代码开发的进度,非技术人员通常是没有直观感受的,那些在开发人员眼里3~5分钟就可以完善的细节,在非技术人员看来可能与最终结果存在非常大的差距,从而引发其对开发人员工作质量的质疑。比如,一个需要展示横幅广告(Banner)图片的地方,如果展示的是一幅无关的图片,那么在技术人员看来完成度已很高,只需要等设计师给出横幅广告的图片直接替换就可以了;但如果演示的时候展示的是一个灰色的矩形,非技术人员可能就会认为代码开发工作还没开始做,因为他们可能无法评估“把灰色矩形替换为横幅广告图片”所对应的工作量,他们更倾向于认为灰色矩形和图片是两种完全不同的东西。所以哪怕使用Mock数据,也请让对外演示的产品尽量贴近真实的样子。观看演示的人给出的反馈很有可能会对项目甚至你未来的职业发展产生影响,你需要让别人理解自己正在做的事情进展如何,如果已经做了90分的工作,就不要用一个看起来只有60分的演示来呈现。


[1]官网地址:http://mockjs.com/