最近在做 IOS 自动化测试,IOS 的 Appium 环境都配置 OK,Demo 脚本运行没有问题,多开执行没有问题,IOS 安卓统一平台调度集成没有问题,可以进行自动化测试。
可是真正执行用例时发现个严重问题:执行速度过慢,慢的像中国男足,Appium 的执行 log 满屏刷,找元素像男国足球场一样骚,一个像无头苍蝇满场跑,一个有模有样的一轮又一轮的查找,就是命中不了。
很是蛋疼,一直疼了好几天,都有放弃的念头了。
Xcode9 SDK 不再支持 snapshot 功能了,没有 snapshot 功能就无法获取 page_source。而从 Appium1.6.5+ 后,Facebook 用 WDA snapshot, 相比于 Xcode SDK 的 snapshot, WDA snapshot 在生成 page source 的时候包含了一个之前没有的属性, 也就是 visibility 属性。计算元素的 visibility 在 XCTest 中是非常痛苦和昂贵的操作。
xpath 好像每次查找时都是重新生成一棵树,WDA 需要额外的努力来实施 XPath 查询,这会严重影响查找时间遍历整个元素树,生成一个 xml 数据,然后再做 xpath 查找。遍历和在 xml 中进行 xpath 查找都相当耗时。论坛比较多的说法是查找元素策略的性能从高到低排列如下:Class Name>Accessibility Id>Link Text>Predicate>Class Chain>XPath
但是以上各种方式试过了,也没有让我的用例快起来。
搜索了很多帖子,很多人都是反映 IOS 自动化速度太慢。但是都没提怎么解决。由于不想改现在共通的滑动和安卓手机按键操作的代码,没有 java-client 升级,保留当前 5.0.4 的版本,没有想到掉这么个大坑里,搞了两三天百撕不得骑姐。
和 py 最新库执行相同用例后的速度对比后才发现,可能是 jar 包的原因。升级 java-client 版本 5.0.4-->6.1.0,升级前来执行 6 分钟左右的下单用例,升级后 2 分钟 (包括人为设置的等待时间) 不到就执行完成了。
更换版本后真的快的飞起啊,比安卓用例执行更快了。
这里说一下我的环境配置,方便速度慢的同学参考:
长的 Xpath 定位可以使用谓词定位,Accessibility Id 定位等来逐步缩小搜索的范围,曲线救国。如下例:
IOSElement inputEL = driver.findElementByXpath("//XCUIElementTypeNavigationBar[@name='LVMMTabBar']/XCUIElementTypeStaticTex");
转换成
IOSElement barEL = driver.findElementByIosNsPredicate("type == 'XCUIElementTypeNavigationBar' AND name == 'LVMMTabBar'");
IOSElement inputEL =barEL.findElementByIosNsPredicate("type == 'XCUIElementTypeStaticText'");
但是由于有些复杂的 Xpath 很难完全将 Xpath 的定位方式转换成 Class Name,Accessibility Id,Predicate 或者 classChain 定位,所以 Xpath 还是不能抛弃的。
只能说尽量不使用 xpath 特别是页面元素比较多的时候。不到万不得已,尽量不用 (页面元素少,速度还可以接受时,可以不转换,灵活运用,转换前后性能自行比较一下)。
各种查找元素策略的性能从高到低排列如下(未一一按时间来具体验证):
Class Name
AccessibilityId
Link Text
Predicate
classChain
XPath
使用方法这里就不一一赘述了。很多同学估计对谓词不了解,也是 java-client 版本 5.0.4 以及以后版本才可以使用的,用下来感觉蛮好的的。
可以看一下全网最好的谓词扫盲贴---->谓词大法传送门
例如通常我们知道"name='https://testerhome.com'"的元素只有一个时,尽量使用
IOSElement niubiEl = driver.findElementByIosNsPredicate("type == '最牛逼的测试交流论坛' AND name == 'https://testerhome.com'");
不要使用
List<IOSElement> niubiEls = driver.findElementsByIosNsPredicate("type == '最牛逼的测试交流论坛' AND name == 'https://testerhome.com'");
IOSElement niubiEl = niubiEls.get(0);
同类的 findElementByAccessibilityId 和 findElementsByAccessibilityId 等
通常情况下 findElements 方法会遍历这个页面去找出所有匹配查询的页面元素,而花费更多的时间, 因为 findElement 不会,而是仅仅返回第一个匹配查询的元素(机器是不会喊累,但也请不要让人家干无用功)。
很多人喜欢封装,但是过度封装,使用很多通配符,以达到万能找元素的效果,但是可能带来的后果就是执行效率低。例如:
List<IOSElement> comnEls = driver.findElement("//*[contains(@name,'" + targetParam+ "') or contains(@label,'" + targetParam+ "') or contains(@value,'" + targetParam+ "')]");
调用是简单了,只需传个参数 targetParam 就好了,但是这需要扫描每个 UI 元素的所有 name,lable,value 属性 (上面属性还可以继续加,达到万能匹配), 无疑这是极度低效的。
对于爬虫来说,可能需要爬取更多相关的东西,设置上面的封装查找是可以的,但是我们自动化来说,我们的目标很明确,而且对执行速度也是有要求的。老老实实的的使用
IOSElement niubiEl = driver.findElementByIosNsPredicate("type == '最牛逼的测试交流论坛' AND name == 'https://testerhome.com'");
则更高效。
尽量减少和服务的通信
if (driver.isElementExist("//XCUIElementTypeButton[@name='未选择']"))
{
driver.findElement("//XCUIElementTypeButton[@name='未选择']").click();
}
上述如果存在时,则需要和 Appium 服务通信两次,查找元素两次(也许例子不恰当)。不如把元素存在与否判断的逻辑放在自己代码里如下:
IOSElement selEl = driver.findElement("//XCUIElementTypeButton[@name='未选择']");
if(selEl != null)
{
selEl .click();
}
升级后一些方法无法正常使用或者过时了。
点击升级前
new TouchAction(driver).tap(x , y).perform();
点击升级后
new AndroidTouchAction(driver).tap(PointOption.point(x , y )).perform();
安卓和IOS分开了
new IOSTouchAction(driver).tap(PointOption.point(x , y )).perform();
滑动升级前
new TouchAction(driver).press(beginX, beginY).waitAction().moveTo(endX, endY).release().perform();
滑动升级后
new AndroidTouchAction(driver).press(PointOption.point(beginX, beginY)).waitAction().moveTo(PointOption.point(endX, endY)).release().perform();
安卓和IOS分开了
new IOSTouchAction(driver).press(PointOption.point(beginX, beginY)).waitAction().moveTo(PointOption.point(endX, endY)).release().perform();
Android 按键操作
升级前
driver.pressKeyCode(AndroidKeyCode.HOME);
driver.pressKeyCode(AndroidKeyCode.BACK);
.
driver.pressKeyCode(AndroidKeyCode.KEYCODE_NUMPAD_9);
升级后
androidDriver.pressKey(new KeyEvent(AndroidKey.HOME));
androidDriver.pressKey(new KeyEvent(AndroidKey.BACK));
.
androidDriver.pressKey(new KeyEvent(AndroidKey.NUMPAD_9));
升级版本后总于解决了执行龟速的问题,蛋也不疼了,如同梦中看到国足秒杀欧冠一样的快意,看着执行速度快的飞起,简直要 GC 了。
这些遇到的一些小坑,总结分享一下。大神轻喷,分享给那些和我一样 low 的人 (看到很多人 Q 群里问环境搭建,Demo 跑不了,求视频,还有很多付费培训广告),我感觉分享还是很有必要的,毕竟大神麼就你们几个。
关于 IOS/Android 自动化的执行速度问题,你有什么骚操作,欢迎一起交流探讨。