写点什么

Flutter UI 自动化测试技术方案选型与探索

  • 2021-06-11
  • 本文字数:2693 字

    阅读完需:约 9 分钟

Flutter UI自动化测试技术方案选型与探索

Flutter 页面无法直接使用 Native 测试工具定位元素,给自动化测试带来很多不便。虽然 Google 官方推出了 Flutter driver 和 Integration test,但是在实际使用中存在以下问题:

  • 不适用于混合栈 APP,虽然 appium 中有相关的 driver,但是无法切换环境。

  • 元素定位能力相对薄弱。

  • 依赖于 VMService,需要构建 Profile 或 Debug 包。


基于以上因素,我们并没有直接使用 Google 官方推出的工具,而是选择基于 Native 测试工具去扩展 Flutter 页面的测试能力。本文对 Flutter driver 和 Integration test 的原理和实现进行了分析,同时简单介绍闲鱼在 UI 自动化测试的尝试方案。

Flutter driver

最早接触 flutter 自动化测试时,先尝试使用 appium 框架去驱动 APP,当我们使用 inspect 功能去 dump 页面元素时发现很多元素会被合并成一个区域块,然后点击的时候只能通过 xpath 定位,想定位到某些具体的元素会比较困难,并且 xpath 其实是容易改变的,代码可维护性能力差。


因为上述原因,我们开始调研 Flutter 官方提供的测试工具——flutter driver。一开始使用该框架的时候发现它只能适用于纯 Flutter 应用,对于混合栈应用并不适应,但是它底层提供的元素定位能力或许对我们有用,于是我们对它的源码进行了剖析,该框架的原理图 1 如下所示。

null

图 1 flutter driver 原理图 


整个框架的流程交互比较简单,测试脚本在运行时,首先利用 FlutterDriver.connect()来连接 VMService 获取相关的 isolate,之后通过 websocket 来传输操作过程以及数据获取。其中测试脚本侧的所有操作都是被序列化为 json 字符串通过 websocket 传递给 ioslate 来转换为命令在 APP 侧执行,例如我们想要获取某个组件的文本内容,其最终生成的 json 结构体如下:


{    "jsonrpc":"2.0",    "id":5,    "method":"ext.flutter.driver",    "params":{        "finderType":"ByValueKey",        "keyValueString":"counter",        "keyValueType":"String",        "command":"get_text",        "isolateId":"isolates/4374098363448227"    }}
复制代码


了解上述原理后,就可以通过构造协议格式,在任何语言、测试框架下都能够去驱动 flutter 测试,所以我们对这个协议进行了封装,使用 Python 进行驱动,这样可以在使用 uiautomator2 和 facebook-wda 的基础上来测试 flutter 页面,以满足 flutter 混合栈应用的测试需求。最终的实现代码 demo 如下。


from flutter_driver.finder import FlutterFinderfrom flutter_driver.flutter_driver import FlutterDriverimport uiautomator2 as u2
if __name__ == "__main__": d = u2.connect() driver = FlutterDriver(d) if pageFlutter is True: # 如果是flutter,则使用flutter driver进行驱动 driver.connect("com.it592.flutter_app") finder = FlutterFinder.by_value_key("input") driver.tap(finder) time.sleep(1) print(driver.getText(FlutterFinder.by_value_key("counter"))) else: d(text="increase").click()
复制代码


我们尝试使用该套框架,发现其实 flutter driver 底层提供的能力相对比较薄弱,并不能完全满足我们的需求,主要问题如下:

  • 不能批量操作元素,一旦 finder 定位到的元素超过 1 个时,就会抛出异常。

  • 很多时候开发同学不写 key,元素定位也没那么方便。

  • 因为 flutter 没有 inspect 工具 dump 元素,所以只能利用结合源码去写脚本,代码维护成本比较高。

  • 官方已经放弃维护该项目,所以后续估计也不会有新功能支持。

integration_test

前面提到,flutter 官方放弃维护 Flutter driver,并推出新的测试框架 integration_test,那么这个框架会不会对混合栈应用予以支持呢,事实上试用了之后发现事情并没有我们想的那么美妙。在官方文档里有这么一句话“该软件包可在设备和模拟器上对 Flutter 代码进行自驱动测试”。


integration_test 底层的元素操作和定位还是基于 flutter_test 去驱动的,其优势主要如下:

  • 测试脚本可以使用各种 Flutter 的 API。

  • 打包 ipa、apk 后就能在 Firebase Test Lab 等设备群上运行测试,不需要额外驱动。

  • integration_test 的每个页面之间测试无关联,可以实现单个页面级别的测试。


但是由于底层元素定位和 Flutter driver 的是一致的,所以 Flutter driver 存在的问题依旧存在,同时还存在其他局限问题:

  • 测试脚本打包到 APP 中,每次修改脚本都需要重新打包。

  • 对端到端测试不够友好,需要额外函数来等待数据加载完毕。

  • 不适合全链路级别的页面测试。

  • 可扩展性弱


基于以上问题,不满足我们的使用需求,所以我们只是做了简单预研,并没有深入了解和应用。

闲鱼 UI 自动化测试方案

学习 Flutter 官方推出的相关测试框架之后,我们开始思考闲鱼 UI 自动化到底要怎么走?是站在官方的肩膀上去造轮子还是复用现有的原生自动化测试能力去扩展 Flutter 测试能力。在综合考虑投入成本以及测试脚本的维护难度后,我们选择使用图像处理技术来扩充原生自动化框架对 Flutter 页面的测试能力支持,整个测试方案架构如图 2 所示。

null

图 2 闲鱼 UI 自动化测试方案架构

 

Flutter 的元素不是完全不能被 uiautomator2 和 facebook-wda 识别,所以编写测试脚本时只需要处理不能被识别的元素即可。对于有 name、label 以及 xpath 不易改变的元素定位,我们优先使用原生定位能力进行定位操作,其他元素则直接使用图像处理技术进行定位操作。


在处理无法使用原生能力定位的元素时,我们优先使用 ocr 文字匹配来进行定位,准确率较高,不容易受分辨率的影响,对于纯图片则通过图片查找的方式进行定位。对于一些常见的元素控件例如商品卡片、价格、icon、头像等,我们构建一个训练集,使用图像分类来判断元素的类型,从而实现常用控件的定位。

UI 自动化面临最大的问题就是——随着版本的迭代,测试脚本也需要进行不断迭代。所以在方案选型和脚本编写过程中需要考虑到脚本的健壮性以及可维护性。我们在实际脚本开发中将页面元素封装到单独的类中,并与测试逻辑分离,从而保证后期元素迭代时只需要修改对应的页面元素即可,减少维护成本。


null

图 3 脚本分层结构 


闲鱼性能自动化测试的相关 UI 操作已经使用该方案,在脚本编写时,并不需要区分当前页面是什么类型。我们的脚本已经稳定运行 500+次,成功率超过 98%。

总结

null

图 4 方案对比 


从图 4 可以看出,无论是 flutter driver 还是 integration test 对混合栈的支持不够成熟,但是 flutter driver 可以进行一些扩展,对于纯 Flutter 应用而言,采用该方案能够基本满足测试需求,而 integration test 相对没有那么成熟,对于混合栈应用的测试,可能还是需要考虑混合栈的场景切换成本,使用一些 ocr 技术去做一些扩充可能成本更低,收益更大。


本文转载自:闲鱼技术(ID:XYtech_Alibaba)

原文链接:Flutter UI自动化测试技术方案选型与探索

2021-06-11 13:004018

评论

发布
暂无评论
发现更多内容

pandas apply 应用套路详解

披头

产品经理训练营第九周作业

Denny-xi

产品经理

当 ITOA 遇上 Cloud Alert,企业可以至少每年节省 3600 小时!

睿象云

智能告警

AIOps 让「事件管理」变得更加智能

睿象云

AIOPS 告警管理

【OpenYurt 深度解析】边缘网关缓存能力的优雅实现

阿里巴巴云原生

容器 云原生 k8s 边缘计算 Go 语言

零代码实现一对一表关系和无限主子表级联保存

crudapi

API crud crudapi 主子表 多对多

SQL 自连接的魅力

披头

区块链落地应用瞄向海洋生态可持续场景,来看Trace Protocol如何改变人们生活?

CECBC

渔民

区块链和物联网如何实现万物互联?

CECBC

区块链

RUOYI 框架教程 12| 若依视图解决多表查询,就是这么简单!

Java_若依框架教程

Java 技术 Ruoyi 框架 若依

统一元数据,数据湖Catalog让大数据存算分离不再是问题

华为云开发者联盟

大数据 元数据 存算分离 华为云MRS 数据湖Catalog

阿里巴巴研究员叔同:云原生是企业数字创新的最短路径

阿里巴巴云原生

云计算 容器 微服务 开发者 云原生

一个提高领导力的极简工具

石云升

领导力 28天写作 职场经验 管理经验 3月日更

第十三周作业

Geek_mewu4t

Java锁总论

邱学喆

Java 锁机制

第12周课后练习-数据应用(一)

潘涛

架构师训练营 4 期

华为帐号服务学习笔记(一):什么是HMS,什么是华为帐号服务

Coding狙击

android HMS

Rust从0到1-所有权-概念介绍

rust 所有权

简单架构图

李朋

架构 架构图

gorm mysql表关联的一个例子

werbenhu

Apache IceBerg表规范(最全版)(翻译者:聚变)

聚变

大数据 hive 数据湖 iceberg 聚变归来

2021年企业需要了解的和云计算相关的6大趋势

云计算

华为帐号服务学习笔记(二):OAuth2.0协议详解

Coding狙击

android 华为 OAuth 2.0 HMS

大厂面试必问!2021新一波程序员跳槽季,附大厂真题面经

欢喜学安卓

android 程序员 面试 移动开发

数字化浪潮下,“坐不住”的豪车品牌如何破局?

脑极体

Linux后端服务器网络编程之线程模型丨reactor模型详解

Linux服务器开发

reactor 后端 网络编程 Linux服务器开发 网络io

面试官:什么是死锁?怎么排查死锁?怎么避免死锁?

小林coding

多线程 操作系统 死锁

ndk开发前景,某大厂开发者对于Android多线程的总结,系列篇

欢喜学安卓

android 程序员 面试 移动开发

ZooKeeper 会话的秘密

HelloGitHub

Java zookeeper ZooKeeper原理 zk

Uniswap v3揭开真面目NA公链(Nirvana)NAC公链表示不服

区块链第一资讯

教育部:探索推动区块链技术在招生考试、学历认证等领域的应用

CECBC

教育管理

Flutter UI自动化测试技术方案选型与探索_架构_闲鱼技术_InfoQ精选文章