appiumpro 用图片查找元素 Part 2

AppiumPro · November 19, 2018 · 717 hits

通过图像查找元素 Part 2

Java 全平台 全设备

这个两部分的系列讲述的是在Appium中某一图像与屏幕区域是如何交互的,这是Part 2。
如果你还没有读过Part 1,那就先去读吧!

在本专栏中,我们会看一些Appium的“按图像查找”特性自带的高级技术用法,这些用法由-image定位器策略来提供。为什么我们需要这些“高级”的技术用法呢?棘手的是图像识别有点复杂,很多事情会影响图像的成功匹配。

例如,你可能会指定一个比设备截图尺寸大的参考图像。这会导致图像匹配算法(在OpenCV库中实现)崩溃(因为如果参考图像比屏幕快照还大,怎么在屏幕快照中查找参考图像呢?)或者,如果你通过图像找到的元素,在找到它和对它发起tap命令的这段时间间隔里位置发生了改变,该怎么办?你最后点击的不过是一个和你的参考图像不匹配的屏幕坐标!

对于上述的每种情况,Appium都可以运行一些逻辑计算来帮助你解决问题(例如,为你缩放参考图像,或者当你点击图像元素时自动重新查找图像元素,必要时更新其位置)。Appium可以独立完成这项工作,提供最健壮、最可靠的图像查找体验,无需你费心去了解这个功能的魔力所在。问题是,这些“修复方案”中的每一个都需要Appium花费大量的时间与自动化引擎工作或沟通。为了加快速度,默认情况下只打开基本的图像元素查找功能。因此,让我们来看看都有哪些可选性,无论如何,基本功能是不够的。

这些可选项中的每一个都可以在Appiumd的Setting API找到。这是一个只有在Appium(不是Selenium)才能找到的API,它能够在测试过程中切换或重置capabilities的设置。它与Desired Capabilities有相同的参数类型,只是它允许Appium客户端在任何时候,任意次数地更新设置,只要你喜欢。在Java客户端中,Settings API隐藏在HasSettings这个接口里。如果我们有一个AndroidDriver或IOSDriver对象,客户端会实现这个接口并对外暴露setSetting方法。(另一方面,如果我们有一个AppiumDriver,首先我们需要对HasSettings进行分类)。

用法相当简单:

driver.setSetting(setting, value);

基本上,我们提供一个setting的名称(实际上是Setting的枚举)和一个value。我们来看看我们如何使用这个模块来寻找图像元素。

##改变图像匹配阈值
在OpenCV,图像匹配的结果不是二进制的0或者1。相反,是在0到1之间的一定程度的匹配。匹配度本身是任意的,但是1代表像素和像素之间的完美匹配,而0代表没有可比性。通过一部分实验之后,Appium的默认匹配阈值设置为0.4。这意味着,默认情况下,相似性度量小于0.4的任何匹配都会认为是失败的。
无论是什么原因,你可能最终会处于0.4,太严格(你找不到元素)或者不够严格(即使元素不存在,也会给你匹配)的情况。你可以使用IMAGE_MATCH_THRESHOLD设置来调整:

driver.setSetting(Setting.IMAGE_MATCH_THRESHOLD, 0.2);

你怎么知道究竟要放什么值呢?你必须多试几次,因为度量本身是随机的(而且是非线性的)。幸运的是,你可以使用上述的Settings API来针对不同图像元素更改查找操作的阈值。

改变图像元素的tap策略

一旦你找到了一个图像元素,并且调用它的element.click()方法,Appium是怎么知道如何点击元素的呢?不幸的是,这里没有魔法可施。Appium只是简单地匹配屏幕区域的边界,并在这些边界的中心坐标上点击而已。当然,Appium有几种不同的方式可以用来点击一个点,例如使用W3C Actions API或旧的Touch Actions API(参见Appium Pro版本的开头,来了解这两个API背后的历史)。
如果Appium默认的tap策略(使用的是W3C Actions)不适合你(比如你使用的驱动程序尚未更新来支持W3C Actions API),你总还是可以用回旧的API,用IMAGE_ELEMENT_TAP_STRATEGY这个设置:

driver.setSetting(Setting.IMAGE_ELEMENT_TAP_STRATEGY, "touchActions");

(两个有效的选项是"w3cActions""touchActions")

解决屏幕快照和设备尺寸不匹配的问题

Appium的图像匹配算法会对两个图像进行操作:一个基础图像(我们要定位的元素就在其中)和参考的图像或模板(和我们试图找到的元素相对应的图像)。你不用担心基本图像:Appium只会用到你设备上的屏幕截图,因为它代表的是屏幕上的实时情况。但是如果基础图像(屏幕截图)和屏幕本身的尺寸不一样呢?匹配算法返回的匹配坐标是对应于屏幕截图,而不是设备。不幸的是,tap操作最终是发生在设备上,这意味着tap的位置会和预想的不一致。
为什么屏幕截图会和屏幕本身的尺寸不匹配呢?原因有很多,尤其是每个平台处理像素缩放的方式不同。例如,iOS先定屏幕宽度是375像素(“逻辑”宽度),然后又高兴地生成宽750像素(“视网膜”宽度)的屏幕截图!
正确地匹配这些维度是非常重要的,因此Appium在默认情况下会做掉。但是,如果你不想让Appium花费CPU时间去匹配,你可以选择放弃:

driver.setSetting(Setting.FIX_IMAGE_FIND_SCREENSHOT_DIMENSIONS, false);

解决参考图像尺寸的问题

如上所述,参考图像(模板)的尺寸必须小于屏幕截图,这样匹配算法才能正常运行起来。有时,当我们生成参考图像时(比如通过手动截取一个元素的屏幕截图),我们不能完全确定模板的尺寸。这可能是由于我们生成模板的方式导致它的尺寸大于Appium截取的屏幕快照的尺寸。
如果你希望可以使用模板图像,无论它的大小如何,请让Appium知道通过以下设置可以调整其大小:

driver.setSetting(Setting.FIX_IMAGE_TEMPLATE_SIZE, true);

默认情况下,Appium不会去做调整,因为它可能会隐藏掉关于可能错误的模板的有价值的反馈(更不用说调整大小耗费一些计算时间和精力)。

##过期图像元素的检查

你可能从Selenium中了解过“过期元素”的概念。过期元素指的是,在发现它和将要和它交互的时间间隔里,不知为何它消失了。在这种情况下,你期望的交互是不可能成功了的(tap、send keys等),因此会抛出StaleElementException的异常。
同样的情况也可能发生在图像元素上。如果元素(由图像匹配算法返回的一组坐标)在查找和点击之间消失了,该怎么办?因为这将导致Appium在元素的过期坐标上tap,所以无论何时请求tap操作,Appium都会再次尝试验证是否匹配。如果再次匹配成功了,并返回与以前相同的坐标,那么就进行tap操作。如果匹配根本不成功,或者坐标不同了,那么就返回StaleElementException的异常。
如果你想优化性能,并取消这个安全检查,你可以随时通过下面的代码关掉它:

driver.setSetting(Setting.CHECK_IMAGE_ELEMENT_STALENESS, false);

##自动刷新元素

默认情况下,如果图像元素的坐标已经过时,Appium会抛出异常让你知道。但是如果元素没有消失,只是改变了位置呢?也许干脆在新的坐标点击它也行了。如果你希望Appium在请求点击时自动刷新图像元素的匹配位置时,可以使用UPDATE_IMAGE_ELEMENT_POSITION来设置:

driver.setSetting(Setting.UPDATE_IMAGE_ELEMENT_POSITION, true);

通常情况下设置为false,来保持正常的情况:当查找图像和tap图像的时间间隔里情况发生了变化,你有办法知道。

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