本文基于的Automation Engine是XCUITest for iOS。
iOS11发布以后,我意识到我不得不升级了, 毕竟让用例继续在iOS9上执行并不是一个好的选择。于是,我花了一段时间,升级到了python3 + Appium1.7.2 + Xcode9.2, 当然过程不会像我描述的那样简单。当我看到用例终于可以运行在iOS11.2上时,我觉得一切都是值得的。可是,我遇到了一个新的难题,用例的执行速度比原来慢的多得多。 一个普通的用例都需要耗费300秒左右的时间,显然这对测试时间和测试稳定性来说都是难以接受的。而最终我完美的解决了这个问题, 于是便有了这篇文章。严格来说,本文不能算是原创,只是重新组织了下自己和别人的心得。
一番搜索,我找到了两个导致用例执行速度突然降低的可能原因:
简单来说, 其实就是Xcode9 SDK不再支持snapshot功能了,但是没有snapshot功能就无法获取page_source, 所以WDA team决定自己启用snapshot。而从Appium1.6.5+后,就开始包含WDA snapshot, 相比于Xcode SDK的snapshot, WDA snapshot在生成page source的时候包含了一个之前没有的属性, 也就是visibility属性。计算元素的visibility在XCTest中是非常痛苦和昂贵的操作。
扯了这么多,还是言归正传吧。 准确的说,本文是介绍如何高效的查找页面元素, 换句话说,我们就是要让get_element(s)方法的执行时间达到最短, 而这其实是和定位方式息息相关的。
1. 选择最高效的查找策略(定位方式)
各种查找元素策略的性能从高到低排列如下:
Class Name
Accessibility Id
Link Text
Predicate
Class Chain
XPath
几乎每个做过Web UI Automation(selenium webdriver)的人都知道XPath的查找性能是最低的。但很少有人会彻底放弃XPath, 一方面是因为XPath足够强大,另一方面也是因为这种程度的性能损耗我们无法真正感觉得到。但在XCTest中,XPath有时候会慢到你难以忍受。 因为原生的XCTest并不支持XPath,WDA需要额外的努力来实施XPath查询,这会严重影响查找时间。如果不是没有其它选择,我们应该尽量避免使用XPath。
PS:
(1) 我们几乎无法100%的将使用XPath轴的定位串转换成Predicate或者Class Chain(但变通的方法通常都有)。
(2) 避免直接调用page source。
(3) 当页面包含非常多的页面元素时,XPath查找的性能也会相应降低。而获取Page source的性能会降低到无法忍受的程度(甚至有可能造成异常)。
2. 缩小搜索的范围
table = driver.findElement(By.accessibilityId('table'));
text = table.findElement(By.predicate('type == "XCUIElementTypeTextField" AND visible == 1'))
如果在搜索范围内的UI元素越多,那么搜索花费的时间就会越多, 而默认情况下搜索范围是整个页面。通过缩小特定元素的查询范围能够一定程度上优化查找元素的性能,特别是当多个元素的查找都将在同一个根元素下执行的时候。这种策略可以帮助我们提高查询效率。
3. 如果你只想要一个元素,那就不要查询多个元素
table = driver.findElement(By.className('XCUIElementTypeTable'))
通常情况下findElements方法会比findElement花费更多的时间, 因为findElement不会遍历这个页面去找出所以匹配查询的页面元素,而是仅仅返回第一个匹配查询的元素。
4. 避免太过通用的匹配方法
By.xpath('//*[@*="1"]/parent::*')
太过通用的匹配方法就像上面使用的//*和@*="1", 这需要扫描每个UI元素的所有attributes, 无疑这是极度低效的。
如果修改成By.xpath('//XCUIElementTypeCell[@name="1"]/parent::XCUIElementTypeTable'),那查找性能就会提高许多。但是我们永远也不要忘记第一条: 尽量避免使用XPath, 我们转换成如下的classChain将会更好:
By.classChain('**/XCUIElementTypeTable[$type == "XCUIElementTypeCell" AND name == "1"$]')