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

Yinxl · December 10, 2016 · Last by joweywen replied at October 25, 2018 · 5935 hits
本帖已被设为精华帖!

导语

最近很多团队都在接入移动端的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 基础原理浅析

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

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

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

恒温 将本帖设为了精华贴 10 Dec 22:45

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

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

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

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

赞一个 很好的解决办法啊

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

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

秀莲?

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

Yinxl #11 · December 26, 2016 作者

#9楼 @chenxi.cui 喜爷?

Yinxl #12 · December 26, 2016 作者

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

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

Yinxl #14 · December 27, 2016 作者

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

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

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

小马 win10 macaca app-inspector 安装使用记 中提及了此贴 21 Feb 16:46
17Floor has been deleted

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

hulibo 回复

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

Baozhida 回复

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

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

Yinxl #22 · March 22, 2017 作者
Baozhida 回复

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

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

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

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

需要 Sign In 后方可回复, 如果你还没有账号请点击这里 Sign Up