Appium实现自动化底层是依赖UiAutomator框架,定位元素也是经由UiAutomator处理后转换Accessibility服务对象完成元素查找并且操作。在前面介绍过Appium-Desktop支持的ID、XPATH、ACCESSIBILITY_ID、CLASS_Name定位,对于这四种定位比较容易理解,剩下的ANDROID_UIAUTOMATOR定位在这篇文章介绍。
一、疑问
对于ANDROID_UIAUTOMATOR的使用出于自己的2个疑问
1.为什么Appium-Desktop不支持非常实用的NAME定位?
2.ANDROID_UIAUTOMATOR这种定位方式的价值有多大?
二、ANDROID_UIAUTOMATOR定位使用
对于Appium来说支持ANDROID_UIAUTOMATOR花费的代码量比ID、ACCESSIBILITY_ID、CLASS_Name加起来都多N倍,对于支持ID、ACCESSIBILITY_ID、CLASS_Name非常简单,直接把入参的String内容传入到UiAutomator对应的方法就搞定,但是对于ANDROID_UIAUTOMATOR就需要一大堆的代码处理,看下面代码:
if (by instanceof ById) {
String locator = rewriteIdLocator((ById) by);
return CustomUiDevice.getInstance().findObject(androidx.test.uiautomator.By.res(locator));
} else if (by instanceof By.ByAccessibilityId) {
return CustomUiDevice.getInstance().findObject(androidx.test.uiautomator.By.desc(by.getElementLocator()));
} else if (by instanceof ByClass) {
return CustomUiDevice.getInstance().findObject(androidx.test.uiautomator.By.clazz(by.getElementLocator()));
} else if (by instanceof By.ByXPath) {
final NodeInfoList matchedNodes = getXPathNodeMatch(by.getElementLocator(), null, false);
if (matchedNodes.isEmpty()) {
throw new ElementNotFoundException();
}
return CustomUiDevice.getInstance().findObject(matchedNodes);
} else if (by instanceof By.ByAndroidUiAutomator) {
UiSelector selector = toSelector(by.getElementLocator());
if (selector == null) {
throw new UiSelectorSyntaxException(by.getElementLocator(), "");
}
return CustomUiDevice.getInstance().findObject(selector);
}
从源码来看ID、ACCESSIBILITY_ID、CLASS_Name这些都是UiAutomator支持的定位方式,唯有XPATH、ANDROID_UIAUTOMATOR是需要经常特殊处理的;ANDROID_UIAUTOMATOR的定位经过ElementLocationHelpers、UiAutomatorParser、UiScrollableParser、UiSelectorParser、UiExpressionParser这5个类的一系列方法逐渐分解,最终返回用户想要定位的元素对象(XPATH的源码分析就不在这篇文章叙述),举例说明:
driver.findElementByAndroidUIAutomator("new UiSelector().resourceId(\"android:id/content\").childSelector(new UiSelector().resourceId(\"ymxh.main:id/user_name\"))");
Appium Server需要把入参的String分解,先拿到new UiSelector()通过反射得到该UiSelector对象,再通过反射去调用它的resourceId方法、childSelector方法;有兴趣可以自己去研究一下Appium Server的源码。
1.Text定位
Appium-Desktop已经不支持Name的定位方式,但是使用ANDROID_UIAUTOMATOR依然可以间接的通过text属性来定位元素,这或许就是为什么Appium-Desktop可以放弃Name定位方法的考虑,从上面举例也可以看出ANDROID_UIAUTOMATOR其实可以完全替代ID、ACCESSIBILITY_ID、CLASS_Name方法。
new UiSelector().text("text文本")
new UiSelector().textContains("包含text文本")
new UiSelector().textStartsWith("以text文本开头")
new UiSelector().textMatches("正则表达式")
2.resourceId
new UiSelector().resourceId("id")
3.className
new UiSelector().className("className")
4.description
new UiSelector().description("contenet-des属性")
5.childSelector
父元素下找子元素
new UiSelector().resourceId("ymxh.main.id/spinner_job").childSelector(new UiSelector().resourceId("android:id/text1"))
6.cfromParent
同层元素
new UiSelector().resourceId("ymxh.main.id/webui_auto").childSelector(new UiSelector().resourceId("ymxh.main.id/interf_auto"))
当然使用ANDROID_UIAUTOMATOR定位方式可以非常灵活方便,只要是符合UiAutomator风格的定位都可以使用,想了解更多方法可以参考一下UiAutomator源码中UiSelector类支持的方法,选择使用。文中只介绍了常见的一些方法,请谅解。