• 作者:老汪软件技巧
  • 发表时间:2024-10-02 10:01
  • 浏览量:

导读: 前端自动化测试的难点主要体现在以下几个方面:

前端应用的复杂性和动态性使得测试脚本容易受到界面或代码更新的影响,导致测试用例频繁失效。跨设备的兼容性测试增加了测试的复杂度,难以保证一致的表现。模拟用户交互、处理异步请求和动画等动态效果也是测试中的挑战。

目前,尚没有一个完美的方案能解决以上所有难点。但通过选择合适的工具、优化测试流程,可以减轻这些问题的影响。本文介绍了一套前端自动化测试解决方案,虽然它无法彻底解决所有测试中的难点与挑战,但仍是一种较为可行且有效的方案。该方案通过结合测试工具、最佳实践和适应性较强的测试策略,旨在应对前端开发中的复杂性和动态性带来的问题,提高测试效率的同时,保证较高的测试覆盖率和稳定性。

背景

随着端内核心业务全面 RN 化,迭代速度也由原来的固定周期发版,变成不定时发版,回归测试面临挑战,如何更高效的覆盖业务中已经稳定的 Test Case 这一问题提上议程。经过调研、实践,我们找到了一条适合的路线:使用优秀开源项目 + 自建平台,搭建自己的自动化测试平台。

什么是客户端自动化测试?

客户端自动化测试是一种利用特定的工具和技术,对客户端应用程序进行自动执行测试用例的方法。

如何选择适合的工具?

工欲善其事,必先利其器!业界有许多关于客户端的自动化测试的方案,关于如何选择,我们主要考虑如下几点:

经调研,Appium 和 Airtest 两款测试框架比较适合我们,我们最终选择了 Airtest,因为使用 Airtest 写测试脚本,更加简单、直观,不会给编码带来太多额外压力

下面简单介绍下 AirtestProject,它是由网易游戏推出的 UI 自动化测试解决方案,其项目核心构成如下:

1)Airtest 框架

Airtest 是一个跨平台的、 基于图像识别 的 UI 自动化测试框架,适用于游戏和 App,支持平台有 Windows、Android 和 iOS

2)Poco 框架

Poco 是一款 基于 UI 控件识别 (通过文字匹配方式获取 UI 控件) 的自动化测试框架,目前支持 Android 原生、iOS 原生、Unity3D、cocos2dx、UE4 和 Egret 等平台,也可以在其他引擎中自行接入 poco-sdk 来使用。

3) AirtestIDE

AirtestIDE 是一款跨平台的 UI 自动化测试编辑器 ,内置了 Airtest 和 Poco 的相关插件功能,能够使用它快速简单地编写 Airtest 和 Poco 代码。

以上内容摘自项目官网:/ ,特别感谢项目组对自动化测试做出的突出贡献。

下一个问题,如何使用 Airtest 做自动化测试?

首先需要找一个业务场景,建议这个场景的业务是比较稳定的,避免未来频繁的修改测试脚本。 我们选择的是基金交易流程这一核心业务场景来做自动化测试,开发过程使用 AirtestIDE 编写 Python 脚本,结合 Airtest 的图像识别能力,Poco 对 UI 控件的识别能力,覆盖了交易流程的部分测试用例

我们首先在雪球基金 App 中寻找场景进行实践,雪球基金 App 支持全品类基金交易,拥有投顾服务及丰富的分析工具。雪球社区便于交流互动,资讯丰富。方便用户投资管理,助力做出科学的投资决策。

下面以基金买入流程部分 case 为例,介绍下实现思路:

先看运行效果:

此流程从搜索基金产品开始,到完成基金买入操作,其覆盖的 case 如下图所示:

买入流程测试代码如下,可以看到主流程的测试代码不多,比较简洁。主要是使用了 Airtest 的图像识别能力,加上适当的延迟等待。实践的过程中,我们发现测试环境的接口延迟很不稳定,严重影响脚本执行的成功率,这也是后面想要加入 Mock Server 的原因。下图代码中的延迟已经排除了接口影响,只是为了等待页面跳转、数据加载、弹窗出现的情况

代码 25 ~ 27 行 对应流程中的第 8 ~ 10 步,检查不同输入金额对应的组合支付提示文案是否显示正确。这部分我们使用了 Poco 对 UI 控件识别能力,之所以使用 Poco , 是因为我们发现当目标是详细的文字内容时,图像识别不够精准

在使用 Poco 时,注意 iOS 和 Android 对使用正则匹配组件的方法定义有区别,iOS 用 nameMatches 匹配,Android 用 textMatches,这一点需要做系统区分。上图应用的场景是输入框与提示文案有联动逻辑,并且因为账户余额是不固定的,使用 Poco 获取组件相比图像识别更适合。

在实践过程中,我们发现有的场景仅使用 Airtest 是没法实现对测试用例的覆盖,需要结合 OCR 技术,下面再介绍一个 Airtest 结合 OCR 的应用场景,OCR 三方库使用的是 pytesseract 。

我们应用的场景是在基金开启定投页面,判断定投周期展示与修改的联动逻辑是否正确,具体 case 如下:

case 校验流程及代码实现如下:

使用 Poco 框架获取页面默认展示的定投周期时间(由服务端返回),iOS 安卓实现有区别

def fetchCycleCardStr():
    poco = importPoco()
    currentCycleStr=''
    if isIOS:
        obj=poco(nameMatches="定投周期")
        text1 = obj.get_name()
        pattern = re.compile('(?<=定投周期).*(?=· 下次扣款:)')
        res = pattern.search(text1)
        currentCycleStr = res.group().replace(",","")
    else:
        obj=poco(text="定投周期")
        targetObj=obj.sibling()[0]
        text1 = targetObj.get_text()
        currentCycleStr=text1
    return currentCycleStr

2. 点击定投周期,唤起日期选择器3. 获取日期选择器选中区域截图,即下图红框部分(下面实现代码中保存为 hahaha.png 就是它),然后识别并返回图中的文字

def snapAndOCR():
    
    screen = G.DEVICE.snapshot()
    local = aircv.crop_image(screen, fetchPickerCenterPos())
    pil_image = cv2_2_pil(local)
    pil_image.save('./hahaha.png', quality=99, optimize=True)
    img = Image.open(r'./hahaha.png')
    # 提升文案识别率     
    img = img.convert('RGB')  
    enhancer = ImageEnhance.Color(img)
    enhancer = enhancer.enhance(0)
    enhancer = ImageEnhance.Brightness(enhancer)
    enhancer = enhancer.enhance(2)
    enhancer = ImageEnhance.Contrast(enhancer)
    enhancer = enhancer.enhance(8)
    enhancer = ImageEnhance.Sharpness(enhancer)
    img = enhancer.enhance(20)
    # 给 pytesseract 设置字符白名单,提升识别率
    config = "--psm 7 --oem 3 -c tessedit_char_whitelist=0123456789交易日周月每一二三四五两"
    text = pytesseract.image_to_string(img, config=config, lang='chi_sim')
    return text

前端无人驾驶能测试什么_无人驾驶软件测试_

4. 比较展示的周期与日期选择器默认选中是否一致5. 滚动日期选择器

获取日期选择器选中区域截图(再次调用 snapAndOCR 方法)点击日期选择器右上角确定按钮使用 Poco 框架获取修改后的定投周期(再次调用 fetchCycleCardStr 方法)比较展示的周期与修改后的定投周期是否一致

选好利器后,我们又遇到了新的问题,AirtestIDE 仅支持单机连接,并且未来随着脚本的增加,在 IDE 中是没法直观查看、选择执行脚本,查看各个脚本的执行结果。自建平台的想法由此产生,一个核心问题也随之浮现出来:

我们需要一个什么样的平台?基于 Airtest 搭建有一个界面,可以查看当前可执行的测试脚本,支持选择脚本,执行结果可视化支持单设备串行执行、多设备并行执行自动化测试脚本让测试脚本运行更顺畅,最好能排除接口影响,专注端侧逻辑验证

基于以上需求,我们制定了一个自动化测试平台搭建方案,整体架构及各模块介绍如下:

脚本执行系统

因为平台要围绕 Airtest 搭建,就需要脱离 AirtestIDE,在本地 Python 环境引入 Airtest 相关库,使用代码来执行自动化测试的相关动作,这也是连接中控系统和设备的关键,这部分 Shell 脚本的核心功能和实现如下:

# executor.h
# 入参1:平台(iOS/Android) 
# 入参2:执行脚本名称
# 入参3:指定设备 id
# airtest 设备链接
target_url=android://127.0.0.1:5037$3?cap_method=JAVACAP
if [ $1 = "iOS" ]; then
  target_url=iOS:///127.0.0.1:8100$3
fi
python_bin=python3.9
file_log_path=${your_log_path}
report_path=${your_report_path}
# 执行 airtest 脚本的命令
run_command=$($python_bin -m airtest run $2.air --device $target_url --log ${file_log_path}
# 生成 airtest 报告的命令
report_command=$($python_bin -m airtest report $2.air --log_root  ${file_log_path}  --lang zh --export  ${report_path} --plugin poco.utils.airtest.report)
# 执行
$run_command
$report_command

# ios_install.h
#设备 uuid
uuid= *** 
ipa_path=/Downloads/***.ipa
# 安装ipa
ideviceinstaller -i $ipa_path

# android_install.h
device_id= ***
apk_path=/Downloads/***.apk
# 安装apk
adb -s $device_id install $apk_path

实现了相关脚本,我们再来看看中央控制系统都有哪些功能?

中央控制系统

系统包含前端程序和后端服务,使用者可以直观的选择可执行脚本、执行设备,查看执行结果。根据测试场景,可以开启/关闭 Mock Service,以保证测试脚本的顺利执行

功能设计:

技术栈选择:前端功能:脚本列表展示脚本选择执行方式选择(随机/指定设备)执行记录列表展示,每条记录包含 3 种执行状态:执行中、成功、失败待执行脚本列表展示执行:轮询执行记录列表,更新结果后端功能:

可执行脚本查询

执行记录查询

可用设备查询

脚本执行功能:

收到前端已选择脚本后,根据执行方式执行脚本;存在空闲设备,进入执行队列随机:选择任一空闲设备,执行脚本,没有空闲设备时,进入等待队列指定:指定一台设备执行脚本,若该设备被占用,进入等待队列执行队列中开始执行一个脚本后,向数据库中写入一条状态为 Running 的脚本执行记录脚本执行结束后,查询 Airtest 脚本执行日志,将执行结果更新到数据库系统介绍:

系统前端部分使用 React 编写,UI 组件使用 Ant Design,页面数据由后端提供,选中脚本点击执行,调用接口处理加入队列逻辑,定时刷新待执行脚本与执行记录列表,执行记录中可查看脚本执行的测试报告。界面如下图所示:

在执行记录列表中,每条记录都可以查看测试报告:

系统后端部分使用 Python + Flask 框架编写,给前端提供查询接口、执行接口;使用 MySQL 保存执行记录;使用 Redis + RQ 构建任务执行队列,处理任务的串行/并行执行,其中并行执行是在平台搭建过程中遇到的一个技术难点。下面展开讲讲:如何实现多机并行执行自动化脚本?

Supervisor + RQ 构建多个任务队列,Supervisor 是 Linux 系统中的进程控制系统,使用其服务器组件 Supervisord 启动守护进程,并根据配置文件中定义的程序启动多个 RQ Worker, 每个 RQ Worker 与一台设备绑定,相当于构建一个任务队列

# supervisord.conf
[program:rqworker1]
command=rq worker 设备1 -u redis://127.0.0.1:6379/0 
directory=/Users/snowball/Desktop/AutoTest/FundTest
autorestart=true
[program:rqworker2]
command=rq worker 设备2 -u redis://127.0.0.1:6379/0
directory=/Users/snowball/Desktop/AutoTest/FundTest
autorestart=true

# worker.py
from rq import Queue
device_list = [{'os''iOS''name':'设备1''id':'00008102-000B191E3E86001E'},
               {'os''Android''name':'设备2''id':'R9HR50LGVZKT'}]
device_queue = {}
# 为每台设备配置任务队列
for device in device_list:
    queue = Queue(device['name'], connection=conn)
    device_queue[device['id']] = queue

这里有个关键细节需要注意: RQ 的 Queue 初始化方法中的 name 要与 rq worker 启动命令使用的一致(上面示例中的 '设备 1','设备 2' ),否则队列无法与设备绑定

接口模拟服务

实践过程中发现接口延迟比较影响自动化测试脚本的执行成功率,有优化空间,我们通过引入 Mock Server 来解决这一问题。经调研,我们选择使用 Charles + Mockoon 来搭建 Mock Server。为什么选择 Mockoon 而不是直接用 Charles ?因为它更专业,下面简单介绍一下 Mockoon :

下面再来介绍下我们是如何使用 Charles + Mockoon 的

然后就可以访问到我们自定义的 Mock Json 啦 !

写在最后

前端的自动化测试技术从引入图像识别后,相比以前只能使用代码查找组件位置的方式,在效率上有了很大提升,不过仍然需要结合业务场景编写测试脚本,对于稳定的业务还是有一定价值的。未来希望可以覆盖更多的场景、更多的测试用例,这个过程一定还会遇到问题,我们会继续总结经验,希望还可以沉淀成文章,再次分享。

我们也在持续关注 AI + 自动化测试的方案,尽管目前还没有找到能够全面覆盖业务流程的理想解决方案,但我们对未来充满期待,希望能够搭上 AI 快速发展的列车。如果你对这一领域有深入的了解,欢迎在评论区分享想法,共同交流,集思广益。

目前来看,前端自动化测试还未达到 “无人驾驶” 般的自主化和智能化水平。仍然依赖测试用例和预定义的脚本,需要开发者介入。然而,随着技术的不断发展,未来的自动化测试工具有望变得更加智能化,它们可能不仅限于执行预定义脚本,还能够自动生成测试场景、预测可能出现的问题,甚至根据实时数据自动调整测试策略,具备自主学习和优化的能力,减少人工干预。但是,它们是否可以完全替代人工测试呢?欢迎留言讨论!