Macaca UI 自动化利器 - 为你的应用自动添加控件 ID 探索

Yinxl · 2016年12月10日 · 最后由 LgdCh 回复于 2021年10月18日 · 3414 次阅读
本帖已被设为精华帖!

导语

最近很多团队都在接入移动端的 UI 自动化,相信大家在使用过程中一个很大的困惑就是如何定位页面上的一个控件,从最大化降低自动化的维护成本上考虑,我们希望这个控件 id 是唯一的,相对稳定的,对于安卓 app 来讲,很多控件是带有 resource-id 的,这个是因为开发同学在使用过程中同样需要这个 id 去定位元素,但是对于 iOS 应用,这个问题是比较头疼的,因为不管是 Macaca 底层所依赖的 XCUITest,还是 Appium 所依赖的 UIAutomation,都是通过 accessibilityIdentifier 来定位元素,但是在 iOS 开发中,基本没有开发同学会去设置这个 id,因此造成一个现状是对于 iOS 应用的元素定位,大家需要变换多种策略,比如通过 class,name,xpath 等等其他属性来获取,如果能给 iOS 上的控件设置 id,将能大大的方便 UI 自动化的工作,更进一步,如果这个添加的过程能自动化实现,而不需要开发同学手工添加,就更加事半功倍了。这篇文章便是对这一策略的实践。

技术可行性分析

了解 iOS 开发的同学都知道,所有视图都是 UIView 或者其子类,如果我们能拦截到所有 UIView 初始化的入口,添加生成 id 的处理,就能为所有的 UIView 添加 id 了,这点是可行的,通过 hook UIView 的 load 方法就可以实现。

怎么生成唯一的 id?

现在我们已经确定是可以通过 Hook 的方式添加 id 的,但是这个 id 要如何取值,这又是一个问题,上面导语部分已经讲到,要最大化的降低自动化脚本的维护成本,我们希望这个 id 在单页面内是唯一的,同时又是相对稳定的,不会因为一次版本的升级就变化,这是在思考中最纠结的一个点,因为要找到一种策略满足这个要求真的不是一件容易的事情,后来在网上看到一篇文章,深受启发,详情参考:http://yulingtianxia.com/blog/2016/03/28/Add-UITest-Label-for-UIAutomation/

概括一下其主要思路如下:

  1. 如果控件对应变量是某个类的属性,则取其属性名称作为标签 id。因为代码没有改动,则标签也不会变化。即使代码有变动,也肯定是因为业务逻辑变更导致了界面上的变化,那么测试脚本肯定也是要改的,所以无需多虑此种情况。
  2. 如果控件对象是临时创建的局部变量,同一页面中很有可能有相同名字的局部变量。而且 Objective-C Runtime 无法获取局部变量名称,所以针对此种情况尽量采用其他来源的内容作为标签。比如对于 UILabel(文本控件),可以取其 text 作为 id,对于 UIButton(按钮) 对象,可以取其 title 标题作为 id,对于 UIImage 对象,可以取其资源名也就是 image 作为 id.

实践

实践是检验真理的唯一标准。我们按照对应的思路在我们的应用工程中添加了对应的处理,主要体现为新增两个扩展类,如图所示:

hookId.png

下面我们对比一下在进行 Hook 操作前后对同一个视图的 inspector 的结果:

在处理之前,我们对某 app 进行 inspect,得到视图树如下:

inspector-1.png

在我们添加了对应的几个文件之后,重新编译,然后对 app 进行 Inspect,得到视图树如下:

inspector-2.png

可以看到视图中的控件都已经添加了对应的 id,对应 dom 树中的 rawIdentifier 控件,就是我们需要的标签了,这样我们就可以通过这个 id 来操作这个金额控件。在此之前,我们想点击这个金额控件是不太好办的,因为这个控件没有 id,他的 class 是 StaticText,而这个类在整个视图中有若干个,通过 class 去取很难,xpath 一是太长,二是一旦视图层级变化,xpath 就会变化,维护成本会很高,也不可取,现在我们就可以通过操作这个 id 直接拿到这个控件了,而且这个控件取的是代码里对应这个控件的属性值,除非逻辑发生变化,一般没有人去修改一个变量的名字,维护成本也相对降低了,可见这个方案是可取的。

完美了吗?

通过上面的方案,我们实现了给控件自动生成 id,但是这个方案能完美解决我们的需要吗?

答案是否定的,因为你会发现,视图中有可能存在两个控件,他们的 Id 是一样的!就像下面这个:

登录页获取 “手机号” 控件 id:

inspect-1.png

登录页获取"密码"控件 id:

inspect-2.png

我们会发现对于 “手机号” 和"密码"两个控件,他们的 id 是一样的,这是为什么呢?

这就要回到我们上面讲到的生成 id 的方案,我们讲到,对于一个控件,如果他是对应父类的一个属性,那我们会取他的属性名字作为控件的 id,到这里相信大部分同学已经找到了上面问题的答案:因为手机号这一行和密码这一行,他们是同一个 UIView 的子类,“手机号” 和 “密码” 这两个控件,他们都是同一个属性!

问题已经定位,接下来要怎么做呢?要保证控件的唯一性吗?

如果我们一定要保证每个控件的唯一性,也不是没有办法,最简单的就是我们给同一个属性的控件添加序号标记,根据出现的先后,依次为控件添加编号,就能实现控件唯一的效果。

但是,一定要这么做吗?

这时候,我们要回归到我们的应用场景上来,什么时候会出现这种 id 重复的情况呢,在一个视图中存在同一个 UIView 的多个对象,这种场景,更多的是出现在我们的列表视图中,也就是 iOS 的 tableView 中,对于这样的视图,我们自动化的策略是什么呢?一般情况下,这种视图的数据都是服务端返回的,而我们在对这样的视图进行自动化的时候,无非是下面几种策略:

  1. 遍历点击所有的 cell
  2. 选择某一个特定的 cell 进行操作,比如第 3 行,也就是 Index = 2

在这种情况下,一个唯一的 id 对我们的作用有多大呢,事实上在这种场景下,我们需要的是一个数组,对于同类控件的一个数组,这种场景下同样的 id 对我们反而是比较好用的,因为我们可以拿到这个数组,然后决定做遍历操作还是对某一个索引的控件做指定的操作。

因此,我们的最终策略是保持这种方式,没有最完美的,只有最适合的,that's all!

源文件

https://github.com/Yinxl/iosHookViewId

使用方法:将 hook 目录下的几个文件拖进 XCode 工程下面编译即可

附录

“Macaca 开源社区” 群的钉钉群号: 11775486(欢迎入群讨论,钉钉顶部搜索框搜索群号加入即可)

更多相关文章

UI 自动化框架调研总结
从无到有搭建 Macaca 环境 (forMac)
Macaca-Java 版入门指南
UI 自动化 Macaca-Java 版实践心得
UI 自动化利器 - 为你的应用自动添加控件 ID 探索
Macaca 基础原理浅析

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 23 条回复 时间 点赞

#7 楼 @dongdong 补充一下,这个只需要把对应的两个类拖到工程下就可以了,通过底层 Hook 的方式,对原代码逻辑没有影响,另外,如果担心影响线上的话可以将对应文件放在 test bundle 中,只在 debug 包中引入,线上不引入,既不会有风险,也不会有开发工作量,何乐而不为呢?

求助,pc 桌面版基于 electronjs 的桌面版应用的 UI 页面中,需要使用到鼠标右键的操作,试问鼠标右键的模拟操作 macaca 如何实现呢,api 中未发现,借问求分享哦

你好,楼主,分辨率不一样的时候,低分辨率没有这个控件,高分辨率有这个控件怎么处理

赞一个 很好的解决办法啊

恒温 将本帖设为了精华贴 12月10日 22:45

点赞,有文有代码,直接就能解决问题

好文章,期待落地效果分析。。。

之前有了解过这块,有部分公司是 hook uicontrol 的 action,将动作名称设为 id,id 会更有意义一些,且唯一

思路和方法很赞。这样就解决了开发手动加 id 工作量大又不一定能满足需要的问题了。

后面可以试试看 Android 有没有类似的解决方案。

android 碰到自定义控件就跪了,只能来一个走一个 xpath 了

不现实的想法,用代码去修改被测代码 是不被允许的

秀莲?

多此一举,你难道不知道 xpath 可以直接通过属性定位控件吗。。。

Yinxl #12 · 2016年12月26日 Author

#10 楼 @pt4847 xpath 可以用这个大家都知道的 但是 xpath 有一个问题是一旦视图层级稍有变化 整个链条的 xpath 都要跟着变 这个维护成本是很高的,是出于这个考虑哈

Yinxl #16 · 2016年12月26日 Author

#9 楼 @chenxi.cui 喜爷?

#12 楼 @junhe 按照楼主的方法进行了尝试,发现 id 值仍然为 null。想问问楼主和大家,这样操作后并不一定所有控件都会产生 id 值(不管唯不唯一),是吗?

Yinxl #18 · 2016年12月27日 Author

#13 楼 @wanwan001 用这个方法 只要是 UIView 的对象都会有 id ,除非你的目标对象不是 UIView 的子类,UIControl 或者自定义的 View 有可能没有

#14 楼 @junhe 尝试了下,ios10 上会有问题。并没有 hook 生成 id

左 10 右 ios9
楼主看看 是否有解决方法?

老马 win10 macaca app-inspector 安装使用记 中提及了此贴 02月21日 16:46

非常感谢楼主的分享,很好用,我写了一个tweak插件可以注入你的代码到 app 里面,可以用于测试人员无法修改源码的情况

Baozhida 回复

恩,需要越狱,现在 ios10 以下都可以越狱,如果不想用自己的手机,可以在某宝上面买个 5c 的游戏机,也就几百块钱,都是直接越狱好了的,可以买个 ios9 的

我司的测试机器是不允许越狱的😂 ,而且大部分机器都是 10 了。这个方案自己测着玩行,不适合公司层面使用

Yinxl #22 · 2017年03月22日 Author
Baozhida 回复

这个方案跟越狱和 10 系统都没有关系的。。

亲测,很好用,既不影响项目代码,又多了一种定位方式👍

Yinxl Macaca-Java 版入门指南 中提及了此贴 06月28日 20:08
Yinxl Macaca 基础原理浅析 中提及了此贴 06月28日 20:07
Yinxl UI 自动化 Macaca-Java 版实践心得 中提及了此贴 06月28日 20:08
Yinxl UI 自动化框架调研总结 中提及了此贴 06月28日 20:09
Yinxl 从无到有搭建 Macaca 环境 (forMac) 中提及了此贴 06月28日 20:10
31楼 已删除
hulibo 回复

必须手机越狱么
现在越狱的手机很少了吧

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册