Appium + mitmProxy 实现APP接口稳定性测试

1.背景介绍

  为了保障 App 的稳定性,我们现在有 XMoney 智能遍历测试(崩溃、界面错乱、加载异常等)、UI 自动化(崩溃和业务逻辑验证)、Top1000 小程序遍历(崩溃和业务逻辑报错)、接口稳定性建设(崩溃和业务逻辑验证)。 今天要给大家介绍的是接口稳定性建设,就是在后端返回数据如果不可靠的情况下,App 是否依然可以稳定运行。

  2.实现思路

  方案一

  Mock有专门的Mock平台, 崩溃有专门的崩溃监控平台, 我们只需要把Mock数据下发到手机,如果有崩溃,会被崩溃平台收集到,崩溃会由值班同学分发给研发。

  1. 将接口代理到Mock平台,Mock平台根据response 生成批量的Mock数据

  2. 通过Appium调用App到制定页面, 触发对应请求,请求到了Mock平台后,Mock平台随机下发Mock数据

  3. 重复打开指定页面, 直到遍历完Mock数据

  4. 崩溃平台进行崩溃分发(人工)

优点:

  短时间内可以覆盖大量的mock数据。

  缺点:

  mock数据不可控。

  方案二

  方案一有太多的随机性,为了让下发的数据和下发数据后的结果能够对应, 我们为了控制下发的数据, 我们在方案一的基础上自建Mock server, 通过Appium脚本和Mock server的交互,控制下发数据。

  经调研,Mock server 比较大众的方案就是采用[mitmproxy](https://github.com/mitmproxy/mitmproxy), [mitmproxy](https://github.com/mitmproxy/mitmproxy)可实现 python 脚本的注入, 通过 python 脚本可以拦截请求的 request 和 response 数据,然后篡改数据后返回。因为 mitmproxy 是一个shell工具, 无法直接和python脚本交互, 所以python脚本和mitproxy的交互方式采用的是.ini的形式,互相读取 .ini 文件中的内容来实现数据交互。

  优点:

  mock数据可控制,可以校验预期结果。

  缺点:

  需要准备mock数据, mock数据要经常维护。

  2种方案我们都有应用,方案一较为简单,在集成测试阶段执行,我们不再展开介绍。 方案二稍微麻烦一些,回归测试阶段执行。以下将详细介绍下方案二如何实现。

  3.代码设计

  方案流程

代码示例

  测试Case

  通过Mock更新数据,测试小程序升级的逻辑 (因涉及代码较多,大家可通过完整系统查看)

  测试小程序的同步升级和异步升级逻辑, 修改测试配置后,mitmproxy会监控对应的接口请求, 发现存在要修改数据的请求,则进行response修改后返回给手机端。 手机端验证是否升级成功。

  步骤:

  1. 修改测试配置(通过ini文件和mitmproxy实现联动, mitmproxy会在每次触发接口请求时检测当前的测试配置文件)

  2. 打开手机app

  3. 打开某个小程序

  4. 验证是否升级成功 (示例为把官方小程序Demo升级成为携程小程序)

  1.  @File : test_update.py

  2.   @Author : YangTongGang

  3.   @Desc : 小程序升级(同步更新、异步更新)

 class TestUpdate(BaseTestCases):
  1. 检测小程序同步更新、异步更新

  2.   已适配(iOS & Android)

  3. def setUp(self) -> None:

  4.   # super(TestUpdate, self).setUp()

  5.   self.business.del_sf_app('智能小程序')

  6.   if self.is_android:

  7.   self.business.del_sf_mine_app('智能小程序')

  8.   def __update_action(self, is_sync=True):

  9.    # 把CTS升级成携程, 并修改max age = 0

  10.    # 控制mitmproxy进行不同的测试

  11.    if is_sync:

  12.    test_type = TestType.TestMaxAgeZero

  13.    else:

  14.    test_type = TestType.TestSaveResponse

  15.    self.changeTestType(test_type)

  16.    protocol = CaseManager.official_demo_case().protocol

  17.    self.business.open_swan(protocol)

  18.    if is_sync:

  19.    test_type = TestType.TestSyncUpdate

  20.    else:

  21.    test_type = TestType.TestAsyncUpdate

  22.    self.changeTestType(test_type)

  23.   def test_max_age_force_update(self):

  24.    """测试小程序同步更新"""

  25.    self.__update_action()

  26.    self.business.open_app()

  27.    protocol = CaseManager.official_demo_case().protocol

  28.    if self.business.open_swan(protocol):

  29.    self.assert_validate('汽车票')

 Mitmproxy 启动

通过shell脚本启动mitmproxy并加载 http_handle.py文件,加载的python文件通过重写response或者request来达到监控每次请求的request和response的能力。

 每次收到测试任务时,测试任务都会有相应的配置, 如果发现需要启动代理服务,则会自动调用如下脚本,启动代理, 然后手动配置测试机代理到服务地址,或者把任务打到固定的测试机上。

  1. #!/usr/bin/env bash

  2.   : '

  3.   @File : start_ui_mock.sh

  4.   @Author : YangTongGang

  5.   @Desc : 启动UI Mock服务

  6.   '

  7.   script_file=/CaseTester/HTTPRouter/http_router.py

  8.   path=$0

  9.   current_path=${path%/*}

  10.   current_path=${current_path%/*}

  11.   # 启动mock 数据服务

  12.   file=/SHELL/start_mock.sh

  13.   file_path=${current_path}${file}

  14.   chmod +x "${file_path}"

  15.   sh "${file_path}" ${script_file}

  16.   #!/usr/bin/env bash

  17.   : '

  18.   @File : start_mock.sh

  19.   @Author : YangTongGang

  20.   @Desc : 启动接口Mock服务

  21.   '

  22.   path=$0

  23.   file_path=$1

  24.   current_path=${path%/*}

  25.   current_path=${current_path%/*}

  26.   file_path=${current_path}'/..'${file_path}

  27.   echo "${file_path}"

  28.   local_ip=$(ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:"|head -n 1)

  29.   echo '=========== 代理服务地址 ============'

  30.   echo 'Script:' "${file_path##*/}"

  31.   echo ''

  32.   echo "${local_ip}":8088

  33.   echo ''

  34.   echo '==================================='

  35.   # mitmdump -s "${file_path}" -p 8088 --flow-detail 0

  36.   status=$(mitmdump -s ${file_path} -p 8088 --flow-detail 0)

  37.   if [$? == 0]

  38.   then

  39.    exit 0

  40.   fi

监控response

  注册对应的接口处理模块,遇到对应的接口时执行对应的测试。

  1. """

  2.   @File : http_router.py

  3.   @Author : YangTongGang

  4.   @Desc : 请求中转站

  5.   """

  6.   # 注册handle

  7.   HANDLES = [

  8.    # Core更新请求

  9.    UpdateCoreHandle,

  10.    # APS拉包请求

  11.    PkgAPSHandle,

  12.    # PMS拉包请求

  13.    PkgPMSHandle,

  14.    # Update接口

  15.    UpdateHandle,

  16.    # 我的页面, 下拉二楼的静默更新个数

  17.    GetPkgListHandle

  18.   ]

  19.   def response(flow):

  20.    """接口response"""

  21.    url = flow.request.url

  22.    path = urlparse.urlsplit(url)['path']

  23.    proxy_mock_urls = []

  24.    for handle in HANDLES:

  25.    proxy_mock_urls.append(handle.identifier)

  26.    test_type = get_proxy_test_type()

  27.    if test_type == TestType.TestNone:

  28.    return

  29.    if path not in proxy_mock_urls:

  30.    return

  31.    response_dic = json.loads(flow.response.content)

  32.    for handle in HANDLES:

  33.    if handle.identifier in path:

  34.    handler = handle(response_dic, test_type)

  35.    response_dic = handler.package_dic

  36.    flow.response.content = bytes(json.dumps(response_dic), encoding='utf8')

  37.    return

4.机房设计简介

为了方便理解整个业务设计,顺便把我们的机房设计也给大家简单介绍一下。任务执行一般都是通过流水线派发到指定机房的随机空闲手机,测试完成后直接返回测试报告,测试执行人无需关心中间过程,也无需维护各种脚本。脚本都在机房维护, 避免在本地Appium部署困难, 脚本更新不及时等问题。

5.结语

因为实际使用过程中依然需要大量人工介入,收益较小,所以执行频率不高,且该方案时间略微久远,所以在此只给大家抛砖引玉,开拓下思路之用,后续如有需要可以作为一种备选方案。

 感谢每一个认真阅读我文章的人!!!

作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

软件测试面试文档

我们学习必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有字节大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。

 

          视频文档获取方式:
这份文档和视频资料,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!以上均可以分享,点下方小卡片即可自行领取。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值