【Web技术】321- 花椒接口Mock方案

什么是Mock?

 Mock是指在测试过程中,对于一些不容易构造/获取的对象,创建一个mock对象来模拟对象的行为。早期mock多被用于单元测试/接口测试中,被测试对象依赖其他对象,且这些对象的构造复杂、耗时或者根本无法构造(未交付), 对于单个测试对象,假定其依赖对象的逻辑正确,我们只需要保证测试模块内部逻辑的质量即可。本文主要讲述web接口的mock。

接口Mock用处?

 在实际的软件开发过程中,我们的链路往往是:服务业务A->服务业务B->客户端/前端->测试,整个开发周期里,业务B的人依赖业务A才能联调提测,客户端/前端依赖B的接口有数据后才能开发新的功能,Mock很好缩短了这个过程中等待的时间。

1. 客户端/前端开发联调前置,现今移动端的app多依赖服务端接口的返回来开发app的页面,在接口未开发完成的情况下,需要等待接口的数据来进行开发,这时候完善的的接口mock服务能大大缩短开发联调等待时间。

2. 接口集成测试,部分依赖服务未完成前,利用mock完成本身的接口开发/测试

3. 复杂的场景模拟,复现验证bug的时候,需要先准备比较复杂的数据场景,才能复现一个bug,此时,mock的定制返回,节省了大量数据准备的时间,直接可复现和验证bug

4. 测试时,异常场景的模拟,如长字符串,负数,异常返回等

花椒的Mock方案

 说了这么多,我们来说说花椒的接口Mock方案。传统的接口mock服务弊端有:

1. 需要绑定接口请求的业务服务到指定的mock服务,这样需要wifi的host不停更改,或本机的host不停更改,来切换正常环境和mock环境

2. 绑定host因为是整个域名绑定到mock服务,如果其他接口没有mock会导致很多接口不可用,而走不到想走的场景,且多人公用wifi的情况下,会互相影响

3. 不同的用户对同一接口的请求,期望的返回不一样,无法对用户定制化,也即同时只能满足一个开发或测试人员的mock需求

4. 传统的mock服务大多采用文件编辑mock数据,不易编辑管理

 考虑到上述弊端,我们的接口Mock服务,设计的初衷有以下几点:

1. 如何让客户端/前端开发人员简单易用,不需要太多环境的设置,保证用户能在正式环境和mock环境之间切换

2. 如何支持多用户同时使用,且mock数据不一样的需求

3. 花椒的部分服务是有加解密的,返回的数据是一堆加密串,如何更方便的编辑管理mock返回数据

 花椒的接口mock方案, 主要是采用业务服务器跳转 + mock服务 + 后台管理,同时支持传统的mock服务的使用方式。

Mock1

主要有如下三部分组成:

* Mock后台

 Mock后台是整个mock服务的配置中心, 用户在后台定制自己的接口mock数据,配置需要跳转的用户及接口。开发框架用的是springboot + mybatis + vue,前端页面和后端服务剥离,springboot提供操作数据库的接口给前端vue页面调用,主要文件目录一个vueMockController, 一个数据库操作文件MockMapper,文件目录非常简单,主要功能有:

1. 展示用户的所有mock数据,支持根据作者查询,uri查询,模块查询

/按Uri查找数据
@RequestMapping("findByUri")
public RestResult findByUri(@RequestBody JSONObject request) throws Exception {
    int offset = request.getIntValue("offset");
    String uri = request.getString("uri");
    uri = "%" + uri + "%";
    List<MockData> data = mapper.getMockDataByUri(uri, offset);
    for (MockData one : data) {
        one.decryptData();
    }
    int total = mapper.getMockDataByUriTotal(uri);
    return success(formatPageData(data, total, offset));
}

2. 支持新增,删除、编辑mock数据功能,写入数据根据选择的平台ios/android,判断有无加密,有加密按各自的加密key加密后写requset和response数据到数据库

主要数据库信息如下, encrypt是否加密,platform(请求客户端iOS或Android),model为模块,uid为用户私有标识,request为请求参数(同时支持key=value的form数据,{“key”:“value”}的json数据),response为响应数据

TABLE `mock` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `uri` varchar(255) DEFAULT NULL,
  `request` varchar(500) CHARACTER SET utf8mb4 DEFAULT NULL,
  `response` text CHARACTER SET utf8mb4,
  `statusCode` int(11) DEFAULT NULL,
  `platform` varchar(20) CHARACTER SET utf8mb4 DEFAULT NULL,
  `encrypt` tinyint(1) DEFAULT NULL COMMENT '0无 1经济 2底层',
  `model` varchar(20) CHARACTER SET utf8mb4 DEFAULT NULL,
  `author` varchar(20) CHARACTER SET utf8mb4 DEFAULT NULL,
  `moduser` varchar(20) CHARACTER SET utf8mb4 DEFAULT NULL,
  `server` tinyint(1) unsigned DEFAULT NULL,
  `method` tinyint(1) unsigned DEFAULT NULL COMMENT '0Get 1Post',
  `uid` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL,
)

对应的页面展示如下,同时提供了加完数据后运行查看是否mock成功的功能.

mock3

3. 获取业务服务器所有mock配置,并展示,支持新增,编辑,修改后同步设置到业务服务器

mock2

* Mock Service

 Mock service是mock请求/返回处理的中心模块,主要逻辑是获取跳转过来的用户请求,处理请求数据,根据不同的请求参数,查询数据库里的配置数据,修改response数据为mock数据返回给用户。开发框架是基于git上开源项目moco的二次开发, Moco在git上的开源地址https://github.com/dreamhead/moco, 是一个简单搭建模拟服务器的程序库/工具, 基于java开发的,mock数据通过文件管理,使用方式很便捷简单,但由于本身花椒服务的特殊性,和考虑到可视化管理,我们对moco进行了二次开发,主要改动方向:

1. Mock数据管理新增数据库存储管理方式, mybatis连接数据库,新增一个数据库操作MockMapper文件,新增一个service方法来处理数据库查询合并结果,具体的mybatis的配置如下:

<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <!-- 配置数据库连接信息 -->
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver" />
                <property name="url" value="jdbc:mysql://10.14.*.*:port/db_name?useUnicode=true&amp;characterEncoding=utf-8" />
                <property name="username" value="username" />
                <property name="password" value="password" />
            </dataSource>
        </environment>
    </environments>
    <mappers>
    <mapper class="com.github.dreamhead.moco.MockMapper"/>
    </mappers>
</configuration>

2. 请求处理适应花椒业务,url请求参数和post数据同时处理,匹配mock数据,以及匹配优先级等特殊处理

 修改moco-core工程里com.github.dreamhead.moco.internal下的MocoHandler.java方法doGetHttpResponse(),匹配规则:

  • 优先匹配用户私有的mock数据,uri(接口uri)+ platform + uid + request参数,有则返回
  • 其次匹配非用户私有的mock数据,uri(接口uri)+ platform + request参数,有则返回
  • 忽略请求参数匹配mock数据,uri(接口uri)+ platform,有则返回
  • 忽略端平台匹配mock数据,uri(接口uri),有则返回

此处之所以要做这么多优先级规则,是为了让使用mock服务的接口能正常匹配到数据,大部分使用者在初期并没有私有数据的需求,随着场景的加深,才会设计独有的数据,所以根据使用习惯,做了分层匹配

3. https的方案更改为nginx配置处理业务证书,证书验证剥离mock服务

java -jar mock-1.0.0-uber.jar http -p 3001 -c config.json //启动服务

本机nginx配置:

server {
        listen       443 ssl http2 default_server;
        server_name  *.*.com; //服务根域名
        root         /usr/share/nginx/html;

        ssl_certificate "/etc/nginx/ssl/tongpei.*.*.com_bundle.crt"; //证书
        ssl_certificate_key "/etc/nginx/ssl/tongpei.*.*.com.key"; //key
        ssl_session_cache shared:SSL:1m;
        ssl_session_timeout  10m;
        ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers on;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
           proxy_pass http://127.0.0.1:3001; //跳转mock服务
        }

* 业务服务器跳转处理

 此处的业务服务器是app应用/前端实际使用的服务器,比如登陆服务。业务服务器的跳转处理,主要是根据用户的请求,判断是否需要跳转到mock服务。逻辑处理是在nginx层处理的,用的luascript,提供了两个接口,一个lua/pour,用于提供给mock后台来设置跳转信息,写到Nginx缓存里;一个lua/get,用来获取nginx现有的配置。

1. 提供mock跳转配置设置/获取接口。

示例

 curl 'http://proxy.**.com/lua/pour?project=test&group=mock&ttl=' -H "Cookie: auth=lua" -d 'value={"user/1**":{"4--60":true,"4--61":true}}'

 curl 'http://proxy.**.com/lua/get?project=test&group=mock'
参数

group: 写死为mock,表示mock配置

project: 项目名称,比如live、test等

ttl: 这个配置的过期时间,默认是3600秒

value: 配置信息,是一个json串,详见以下说明

对于配置value的说明

示例

{
    "user/1**" : {
        "4--60" : true,
        "4--61" : "4.4.4.4"
    },
    "user/2**" : {
        "*" : true,
        "4--60" : "4.4.4.4"
    },
    "*" : {
        "*" : "8.8.8.8",
        "4--67" : true,
    }
}

说明: json数组的第一级是url,注意这个url不带前导/,第二级是uid,匹配客户端请求参数中的userid;uid对应的值为mock地址,如果值为true,则转到默认的...(mock服务器) 这两级都可以配置为通配符*,匹配的优先级为先匹配精确的,再匹配通配符 例如上面这个例子中,如果4--61这个用户访问user/1**接口,则会转到4.4.4.4,而4--88这个用户访问的话,会被指到8.8.8.8

2. 根据配置跳转业务请求到mock服务

-- 转到对应的机器
local proxy_res = cache:get(project .. "_proxy")
if proxy_res and proxy_res ~= "" and (ngx.req.get_headers()["x-proxy-host"] == nil or ngx.req.get_headers()["x-proxy-host"] == "") then
    -- 增加错误处理,对于有问题的json啥的,做容错
    local status, proxy_cnf = pcall(function(proxy_res) 
        local cjson  = require "cjson"
        return cjson.decode(proxy_res)
    end
    , proxy_res)
    if status == false then
        proxy_cnf = nil
    end

    if proxy_cnf and type(proxy_cnf) == "table" then
        if proxy_cnf[ngx.var.arg_userid] then
            ngx.req.set_header("x-proxy-host", proxy_cnf[ngx.var.arg_userid])
            ngx.exec("@proxy")
        elseif proxy_cnf[ngx.var.arg_deviceid] then
            ngx.req.set_header("x-proxy-host", proxy_cnf[ngx.var.arg_deviceid])
            ngx.exec("@proxy")
        end
    end
end

使用方法

最后的最后,一切准备就绪,使用就非常简单了

使用方式一的步骤(如图):

1. 新增要mock接口的uri对应的返回数据

2. Mock后台新增跳转配置,如配置user/1**, uid=4--86跳转

2. 手机/前端正常操做请求,即可返回定制数据

mock

使用方式二的步骤:

1. wifi或本机hosts绑定请求接口域名到mock服务器 1.1.1.1 passport.**.com

2. 手机/前端连接绑host的wifi,返回定制数据

 整个方案的过程中,我们也是一直在摸索调整,如:一开始的时候我们也并没有针对用户来做mock数据的区分,使用时碰到开发有多人同时使用的情况,一个人改了数据,另外一个人使用时发现不对了;还有https证书的问题,花椒没有提供moco框架https服务需要的证书,为了Mock服务能同时支持客户端直接绑host的方式,采用ngnix跳转服务的方式,先处理完证书验证,再跳转到mock服务。目前我们的mock服务偏向于给开发人员和手工测试人员提供便捷的模拟服务,mock在自动化测试上的应用还未被完全挖掘出来,有待进一步探讨。

本文分享自微信公众号 - 前端自习课(FE-study)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-08-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据分析1480

写文章不会起标题?爬取虎嗅5万篇文章告诉你

摘要: 不少时候,一篇文章能否得到广泛的传播,除了文章本身实打实的质量以外,一个好的标题也至关重要。本文爬取了虎嗅网建站至今共 5 万条新闻标题内容,助你找到起...

8220
来自专栏方丈的寺院

Mongo连接分析

在前面的文章中有分析过关系型数据库的连接,以及连接池的原理。在mongo数据库同样存在,经常看到有网友在问mongo 连接了数据库要不要关,怎么关。内置的数据库...

13420
来自专栏数据分析1480

Scrapy框架系列--爬虫利器之初体验(1)

为什么要学 scrapy 呢?看下图中的招聘要求,就清楚了。很多招聘要求都有 scrapy,主要是因为 scrapy 确实很强。那到底强在哪里呢?请在文中找答案...

13130
来自专栏方丈的寺院

mongo实现自增id

Mongo 的free schema,提供了灵活的数据结构,和快速开发的能力,但是也造成了松散的数据组织形式。比如说有些字段不允许为null的,需要符合一定格式...

13730
来自专栏方丈的寺院

web开发常用工具

本篇是工具类总结文章,不包含深度分析,仅个人从业数年的一些总结,希望能够对你有帮助。本文不定期更新,如果你有好的工具也可以推荐给我

15520
来自专栏数据分析1480

Python 数据库骚操作 -- MongoDB

最近这几天准备介绍一下 Python 与三大数据库的使用,这是第一篇,首先来介绍 MongoDB 吧,这里介绍 MongoDB 的两款操作库,走起!!

10310
来自专栏数据分析1480

用Python告诉你深圳房租有多高

最近各大一二线城市的房租都有上涨,究竟整体上涨到什么程度呢?我们也不得而知,于是乎 zone 为了一探究竟,便用 Python 爬取了房某下的深圳的租房数据,以...

9420
来自专栏方丈的寺院

数据库连接(2) - 为什么C3P0连接池那么慢

在上一篇中我们介绍说客户端建立一次连接耗时太长(建立连接,设置字符集,autocommit等),如果在每个sql操作都需要经历建立连接,关闭连接。不仅应用程序响...

9410
来自专栏数据分析1480

Python 数据库骚操作 -- MySQL

今天这篇是三大数据库的结尾篇,前面两篇分别是:《Python 数据库骚操作 -- MongoDB》《Python 数据库骚操作 -- Redis》,这篇主要介绍...

9010
来自专栏数据分析1480

6000 多款 App,看我如何搞定她们并将其洗白白~

如果说 GitHub 是程序员的天堂,那么 酷安 则是手机 App 爱好者们(别称「搞机」爱好者)的天堂,相比于那些传统的手机应用下载市场,酷安有三点特别之处:

8520

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励