前端mock小结

  我们都知道,在现在流行的“前后端分离”架构中,前后端的联调,是一个不能避免的问题,特别是在项目稍微有点规模之后,“等接口”似乎成了前端开发者的一种常态。所以通常我们在不借助任何工具的情况下,进行前后端联调大概会出现以下几种尴尬的局面:

  • 前后端开发速度不一致,前端页面写好之后,必需要等待服务端的接口出来,才能有足够的数据支撑UI的显示。
  • 服务端接口不能满足界面的需求时,要前端调用发现问题之后,反馈给服务端,然后服务端需要根据要求修改接口定义,重新部署,前端再重新调用。
  • 前端调用接口的操作成为了测试接口是否有bug的手段。

  以上的情况就造成了前后端联调过程中最主要的问题,沟通成本,时间成本,服务重启的成本加在一起,就是工期经常延后的原因。

一、解决前后端联调的以往做法

  在以往的做法中,解决这个问题的办法通常有两种,一种前端做法,一种后端做法。
  前端的做法一般是在业务代码里面先写上假数据供页面的显示,等后端接口写好之后,再将假数据注释掉,改成接口调用,通常表现为以下的形式:

getData = (id) =>{
 return $.get('/api/user/' + id);
 //return new Promise((resolve, reject)=> {
 //  setTimeout(()=>{
 //    return resolve({
 //      data: {
 //        errcode: 0,
 //        data: {
 //          id: 1,
 //          name: 'tom'
 //        }
 //      }
 //    })
 //  }, 100)
 //})
}

可以看到,这样的做法会在业务代码里面产生非常多的冗余代码,且在真实接口和假数据之间的切换非常不利于维护,前端开发者必须要记住在上线前,把所有的假数据注释掉,切换成真实接口的调用。如果有很多接口都这样做的话,这样大大增加了开发者的工作量。
  后端的做法大多是先写一套测试数据的接口供前端调用,等接口开发完成之后再切换过来。这样无疑又增加了后端开发者的工作量, 而即便是这样,当后端在写正式接口的过程中,增删或者修改了某一个字段,亦或是数据接口发生了改变,这样都不能及时的更新到临时的测试接口上,前端也无法得到及时的反馈,所以会造成前端逻辑处理的很多返工操作。
  这两种方法,无论是哪一种,在实际的应用中都非常不方便,既影响了业务代码,也增加了正常开发的工作量,所以前端开发者在开发的过程中一直在寻求一种mock的方法,能够在开发的过程中,前后端实现完全分离的,彼此不受牵制。

二、mock的概念

  所谓mock,意思就是模拟,上面提到的前端开发者通常用写在业务代码里面的假数据代替还没有开发完成的接口返回的数据的方法,其实就是对前端数据的一种mock方法,只是这种方法不是很方便。于是阿里巴巴的某大神就出品了一款功能非常全面的模拟数据生成的 JS 库,也就是我们现在见到的mock.js,通过它我们可以使用简单的语法模拟各类常见的数据。
  想要在代码里面使用mock.js首先要进行npm安装:npm install mockjs --save-dev,或者直接在script标签中引用:<script src="http://mockjs.com/dist/mock.js"\></script\>,然后还需要了解一些mock.js的语法规则,这些规则都很简单,这里就不介绍语法了,只是举一个简单的例子来表明mock.js对于生成模拟数据来说有多方便。比如现在页面需要如下所示的一个数据结构,但是后端接口还没有开发好,这个时候页面的显示就需要前端自己来模拟一些数据填充。

{
    "name": "BeJson",
    "links": [
        {
            "name": "Google",
            "url": "http://www.google.com"
        },
        {
            "name": "Baidu",
            "url": "http://www.baidu.com"
        },
        {
            "name": "SoSo",
            "url": "http://www.SoSo.com"
        }
    ]
}

  这样看,如果全部手写的话,好像工作量也不是很大。但是如果一旦其中的links数组需要有成百上千条数据的时候,前端开发者一条一条的写,亦或是复制粘贴都变得非常不现实,所以这时候我们可以利用mock.js来写这样一个结构:

Mock.mock({
    "name": "@name",
    "links|100": [
        {
            "name": "@name",
            "url": "@url"
        }
    ]
})

如上面的代码所示,links|100是mock.js的一个语法,表示的是给links数组里面创建100个对象,每个对象都包含name和url两个属性,这两个属性会通过@name和@url这两个mock.js的规则,随机生成名字和链接,当然,这里生成的name是一个随机的英文名。这样再也不需要自己手动复制粘贴改数据,只需要改一个地方就可以轻松的拿到任意条数据。关于mock.js的语法规则可以查看其官网http://mockjs.com
  有了mock.js之后,前端开发者的mock之路又前进了一步,至少在写mock数据的时候更加方便了。其实更多时候的做法是,单独用mock.js写一个data.json的文件,然后在业务代码里面切换真实接口请求和本地data.json文件请求,如下面的代码所示:

getData = (id) =>{
    //return $.get('/api/user/' + id);
    return $.get('data.json');
}

  或者直接写真实的接口,然后请求发出去的时候,利用fiddler或者charles等代理工具,将这个接口Map Local到本地的data.json文件,这样就不用改动业务代码,更加方便易操作。

三、常用的两个开源前端mock工具

  mock.js的出现虽然已经简化了前端工作量,但是依旧有很多问题,或者说它只解决了一部分问题。比如说:

  • 所有接口的mock规则都需要开发者手动定义;
  • 难以模拟出各种不同的网络请求状态;
  • 难以模拟出数据错误,或者空数据的状态;
  • 不能模拟出根据不同请求参数,返回不同结果的情况;

  上面的最后三种状态都是在前端界面的开发过程中都需要考虑到的情况,但是mock.js都比较难以做到,所以这个时候就出现了一些前端的mock工具。这些工具能够确保前端在开发的过程中的模拟数据可控,且在使用之后不会对前端或者服务端的流程有任何影响,使得前后端有一个明确的分界点。常见的前端mock工具有RAP,YApi,NEI,EasyMock,Mock-API等,他们都是基于Mock.js来进行数据模拟的,并在此基础上做了不同的扩展。

1.RAP

  RAP是阿里团队出的一款WEB接口管理工具,帮助开发人员有效的管理接口文档。它是一个企业级应用,包括阿里集团在内的三百五十多个企业都在使用RAP管理重要的接口文档。
  下面是一张RAP的一张开发流程图,其实这张图也基本适用于所有使用mock工具的项目。

RAP开发流程图

  从这张图上我们可以看到,项目的最开始就要先制定接口,并利用mock工具将接口模拟出来,然后前后端分别进行开发,这时候前端是跟mock工具生成的mock数据进行交互的,而后端则开发与测试自己的接口是否正确。在前后端都开发完毕之后,后端再把真实接口部署出来,然后前端把把mock环境切到真实环境,进行联调和测试。此时这个阶段已经不是开发的阶段了,而是开发之后的校验阶段,由此可以看到,利用mock工具,我们至少能在开发阶段做到真正的前后端分离的状态了 。
  回到RAP本身来说,它最大的特色莫过于它的GUI了,良好的可视化界面使得创建接口变的非常的容易。它编写接口的时候只需要按照定好的接口文档,在响应参数列表里面新建需要返回的参数就可以,它会根据你新建的参数,以及设定的参数类型和mock规则,自动生成对应的接口数据,非常的方便。RAP还有一个比较酷的地方就是他能根据用户编写的mock规则校验真实接口的正确性,如果切换到真实的接口之后,返回的数据结构或者数据类型与在mock中填写的规则不一样,它会在浏览器控制台打印出错误信息。
  虽然RAP的可视化界面使得mock操作变得非常简单,很容易就能拿到数据,但是如果要在项目中用到这些数据的话,还是有一些限制的,RAP的使用是通过在项目中引入一行RAP插件代码,利用这个插件来拦截发出去的请求,然后返回RAP模拟出来的数据,当要切换到真是接口测试的时候只需要改动插件代码上的一个参数就可以了,但是遗憾的是现阶段RAP插件的请求拦截只支持KISSY和jQuery,具体使用规则,可以查看其官方文档RAP用户手册

tips:网上有大牛也提供了AngualrJS版的RAP插件,详见:https://github.com/xiaomaojames/ng-rap

2.YApi

  YApi是去哪儿网移动架构组开发的一个开源项目,可以帮开发者轻松创建发布,维护API,相比较RAP,它有以下优点:
  (1)YApi具有扁平化的权限设计,可以满足大型企业级的项目管理,在YApi里超级管理员拥有最高权限,并将权限分配给若干组长,超级管理员只需管理组长,组长又分为分组组长与项目组长,分组组长在创建项目时可以指定项目组长,项目组长可以邀请组员参与项目,共享接口数据。
  (2)YApi在新建项目的时候,还可以快速导入其他格式的接口数据,方便快速添加接口。目前支持postman,swagger,har数据导入。当然首先你需要先在以上三个接口管理平台上导出接口数据,然后再将导出的数据导入到YApi平台上,它会自动解析文件,并生成相应的接口。以导入swagger数据为例:首先要生成 JSON 语言编写的 Swagger API 文档文件,例如这样的数据http://petstore.swagger.io/v2/swagger.json,我们可以将其内容复制到 一个JSON 文件中,然后打开yapi平台,进入到项目页面,点击数据管理,选择相应的分组和swagger导入方式,选择刚才的文件,就可以开始导入JSON数据,最后会生成相应的接口。这个功能比较适合一些成熟的接口,这些接口从一些接口平台导出之后不会再变更的情况下会很方便,同时也适合一些项目的迁移。
  (3)YApi具有接口文档管理的功能,在传统后端的接口开发过程中,接口开发和接口测试是必不可少的,但是接口文档因为没有跟开发和测试联系到一起,所以一旦在开发或者测试的过程中有改动,那么维护接口文档将是一件很繁琐的事情。YApi将接口文档和测试通过单一数据源连接到一起,如果接口改动,因为改的是单一数据源,就不会出现更新不及时的情况,并且YApi的模拟接口还可以关联接口文档,所以当后端在开发的过程中调整了接口文档中的定义时,前端开发者就能及时作出改动。

  YApi在一定程度上来说,比RAP的功能更加完善,也更加强大,它提供接口文档管理,接口数据模拟(Mock),接口调试,自动化测试等一系列功能。
  虽然如此,然而最厉害的不一定是最合适的,在数金,我们通常使用swagger来管理接口,虽然YApi支持导出swagger格式的数据,并解析成mock接口,但是它是通过将swagger文档导出成json格式的文件之后,再将这个文件解析出来,这样就会产生一个问题,当后端同学改动了swagger上的现有接口,亦或是又增加了一些接口,就需要再重新导出一份swagger文档的json文件,然后再导入YApi平台解析一次,这样的操作既冗杂又繁琐,所以对于我们使用swagger来管理接口的团队来说,如果某个mock工具能够有“一键同步swagger”的操作,那将会是减轻不少的工作量。巧合的是,Easy Mock刚好能解决我们这个问题。

四、Easy Mock的介绍

  Easy Mock是大搜车前端团队开发的一个可视化,能快速生成模拟数据的在线mock服务,他没有RAP那么有限制性,也没有YApi那么完善的功能,但是对于我们来说,它刚刚好,在对比了现有的几种前端开源mock工具后,我们决定选用Easy Mock作为我们团队的前端模拟工具,并将它部署在内网。
  与其他的mock工具一样,Easy Mock基于Mock.js,能够使用简单的语法创建出模拟数据,如果你还不了解mock.js的语法,可以访问其官网http://mockjs.com,除了支持mock.js的基本语法之外,Easy Mock还做了一些扩展,例如:

1.支持简单的Function,可以实现逻辑判断,并对结果进行动态的返回。
2.Easy Mock在每个mock接口里面注入了一些express的变量,这些变量就是原来 req 上的几乎所有的属性,如_req.header、_req.query等,有了这些变量,就可以根据用户调用mock接口时传进来的一些信息做非常动态写响应式返回。
3.Easy Mock支持restful风格的接口,例如“/merchandise/:id”,我们可以通过_req.params.id 来获取到参数的值。

  除了在mock.js语法的基础上做了扩展之外,Easy Mock的另一大优势是可以根据swagger中定义的实体的属性类型生成对应的mock数据。由于我们公司大多使用swagger来管理接口,所以在定义好接口之后,可以先利用接口文档部署一套只有数据结构,没有真正实现功能的swagger接口,然后通过swagger文档地址,就可以在Easy Mock中生成一个包含此文档地址数据的模拟接口,此操作支持swagger1.0和swagger2.0的语法。
  由于我们在生成的过程中填入的是一个swagger文档的地址,不是一个静态的json文件,所以当swagger的接口有更新的时候,Easy Mock支持“一键同步”,且这个同步是智能的,它会先检测这个接口是否有变动,如果有则对这个接口做覆盖式的更新,如果没有,则不会更新。这一特性对于不断有需求进来,不断迭代的项目组非常的友好,这也是我们选择这个工具的非常重要的一个原因。
  通过Easy Mock来进行数据模拟对流程不会产生任何影响,只是通过swagger接口文档来生成模拟接口,需要swagger注解做的更加严谨和完善,这样在mock接口中才能看到更加清晰的这个接口的功能,用法等等信息。

Easy Mock的使用方法
  下面我们简单的讲一下Easy Mock的使用方法,以使用swagger的项目为例。如果你没有在内网部署的话,可以进官网,注册一个账号,然后开启你的神奇的swagger+Easy Mock之旅,新注册的账号会自动生成一个演示项目,里面创建了多种的Mock类型,对于新手来说,可以先看一下这个演示项目,在此基础上进行编辑。如果你想创建自己的项目,可以跟着以下的步骤来做:
(1)创建项目
  点击主页右下角的蓝色加号按钮,进入项目创建界面

创建项目

(2)填写基本信息
  因为我们要基于swagger来创建这个项目,所以注意要在swagger Docs Api里面填入你项目的swagger Docs Api的地址(不用swagger的可以跳过填swagger Docs Api这一栏)。

填写基本信息

  注意:项目swagger Docs Api的地址并不是项目swagger的地址,一个通用的获取swagger Docs Api地址的方法就是,将swagger的URL中项目名只有的部分替换成/v2/api-docs,比如地址为https://xxxx.xxxx.com/projectname/swagger-ui.html替换后的地址为https://xxxx.xxxx.com/projectname/v2/api-docs,这样就能得到swagger Docs Api的地址了。
  基本信息填写完毕后,点击创建按钮,即可创建成功。接下来回到项目页面就可以看到我们创建的项目

创建成功

(3)查看接口信息
  点击上图的项目地址,进去之后,你将看到Easy Mock将你swagger中的接口都拉下来了。

创建好的项目

  我们可以看到,上面甚至显示了POST/GET方法,以及一些描述性的文字,这些内容的显示要求写接口的同学定义的更加严谨,swagger注解要写的更加全面。
  在接口信息的最右边有四个按钮,分别是预览,编辑,复制地址以及其他操作,点击预览按钮我们可以看到这个接口生成的模拟数据,如果接口是get方法的话,你将会直接看到返回值,但如果是post方法的话,需要你先在右边的输入框里面先输入你的请求参数,然后才能看到返回结果。
POST请求

POST请求

  因为我们现在并没有写请求参数与返回数据之间的关系,所以不出意外的话,随便输入什么值,都可以得到一个模拟数据。
(4)根据需要完善接口
  我们必须要知道请求一个从Swagger上面拉下的模拟接口时,是不能直接根据请求入参来进行响应式的返回的(否则就不是模拟了),如果要定制化,则需要我们根据Easy Mock的扩展语法手动添加一些function来返回响应式数据,所以虽然模拟数据不能直接做到数据联动,但是借助于响应式数据,也能玩出很多花样。
  我们可以在数据编辑器中,为某个属性指定一个function。在function中Easy Mock提供了_req对象,这个对象我们已经在上面提到过,它是express的一个对象,里面_req.header、_req.body等属性,这使得我们可以通过这些属性来做响应式数据,一个简单的示例如下:

{
data:{
  default:'hah',
  _req:function({_req}){
    return _req
  },
  name:function({_req,Mock }){
    if(req.query.name === 'nk'){
      return _req.query.name + '_' +Mock.mock('@name')
    }else{
      return this.default
    }
  }
}

  除了响应式的返回,Easy Mock还支持自定义的响应状态,通过在返回的结构中添加_res对象,可以给返回添加定制信息,_res对象里面包含_res.status,_res.cookies,_res.headers以及_res.data等,其中_res.data是在_res.status不为200的时候返回的,用来替代正常返回体里面的data。
  至此,我们运用以上简单的四个步骤就可以基于swagger接口创建一个EasyMock的项目。如果swagger接口有更新的话,只需要点击项目右上角的按钮,就可以一键同步。
  项目创建好了之后,就到了使用的环节了,想要使用mock的接口,而不改变业务代码的逻辑,则只需要在发请求的时候用代理工具,例如charles,fiddler等,将对应的请求Map Remote到我们的mock接口就可以了。

五、小结

  在前后端的联调过程中,mock工具并不是必须存在的,甚至有很多同学觉得它的存在并没有带来任何价值,属于“多此一举”的操作,这本来就是很难界定其好与坏,或许对于一些小的项目来说,的确帮助没有那么明显,但是一旦项目比较繁琐,那么前端接口mock将会给开发者带来很多的便利:前端开发者不需要等待后端接口出来之后再调试页面;前端开发者可以根据自己的意愿模拟出各种不同的返回结果来调整页面的显示等等都是显而易见的便利。
  这里提供一个大搜车内部推荐的Easy Mock应用流程,可供大家在使用Easy Mock的过程中参考:

  • 需求,可行性评估
  • 需求评审,通常是交互稿或者原型
  • 交互或者设计评审,确认逻辑细节
  • 开发给出排期,包括(接口定义、前端开发、联调、提测、测试时间)
  • PM或架构师系统和架构分析产出
  • 服务端设计数据库和接口,给出详细的接口定义
  • 接口评审,完成后部署开发服务器,会自动根据swagger定义通过easy-mock服务生产对应的mock项目。
  • 前后端分别开发,前端直接使用easy-mock提供的数据构建前端页面。
  • 联调,测试,运维发布。
      由于内网Easy Mock刚刚搭建好,我们团队也是在这个过程中不断的学习,所以如果大家在使用的过程中有发现任何问题或者疑问,希望可以及时与我们联系,我们一定会尽快给大家答复。

六、其他工具

  最后,如果我们的工具,以及上面提到的RAP和YApi都不能满足您的要求的话,您也可以访问以下的链接来找到你心仪的工具:
NEI:网易的api接口管理平台,功能很完善,但是不开源。
apizza:在线模拟调试,快速生成api文档,导出离线版文档,功能完善,不开源。
AMP:github的mock开源项目。
Mockbin:可以从HAR文件生成一个模拟桩。
Doclever:编写REST接口文档,版本控制,生成测试数据。
apiary:Powerful API Design Stack. Built for Developers。
mocky:无需登录,直接生成一个resposne,url不固定。
mock-api:一个网络服务,用来生成HTTP接口的模拟返回值。

推荐阅读更多精彩内容