通常使用定位工具定位 App 页面上的元素会发生定位不到元素或者定位元素失败的情况。发生这种情况可能是 App 的页面不唯一,也可能是 App 的页面发生了变化。下面介绍定位元素的高级用法,使用层级关系定位或者多重属性定位的方式来确定了 App 页面元素的唯一性,从而更精准、更稳定地定位到我们想要 App 页面元素。
1.XPath 高级定位技巧
(1)XPath 简介
XPath 的英文全称为 XML Path Language,意为对 XML 中的元素进行路径定位的一种语言,它使用与 XML 标记语言、HTML 标记语言,App Dom 结构。XPath 是我们做自动化测试中进行元素定位时用的比较多的工具,它可使用与 Seleniu、Appium 和 Appcrawler。前面章节已经对 XPath 进行了一些说明,下面只对 XPath 做举例说明。
(2)XPath 基本语法
表 5-4 是 XPath 的常用方法。
(3)XPath 模糊定位技巧
XPath 中的 contains()是模糊匹配的定位方法,若在定位元素时,一个元素的属性不固定,这时就可以用模糊匹配来定位元素。使用格式为//[contains(@content-desc,'帮助')],示例代码如下(Python 版和 Java 版)。
Python 版
driver.find_element(By.XPATH,
'//*[contains(@text,"测试")]')
driver.find_element(By.XPATH,
'//*[contains(@content-desc,"搜索")]')
driver.find_element(By.XPATH,
'//*[contains(@resource-id,"login_phone")]')
复制代码
Java 版本
driver.findElement (By.xpath(
"//*[contains(@text,\"测试")]"));
driver.findElement(By.xpath(
"//*[contains(@content-desc,\"搜索"\)]"));
driver.findElement(By.xpath(
"//*[contains(@resource-id,\"login_phone\")]"));
复制代码
(4)XPath 组合定位技巧
用 XPath 可以同时匹配两个甚至多个属性来完成元素定位。这里常用的属性有 text、resource-id、class、index、content-desc 等,任意组合这些属性完成定位,示例代码如下(Python 版和 Java 版)。
Python 版本
driver.find_element(
By.XPATH,'//*[@text="我的" and @resource-id="tab_name"]'
).click()
driver.find_element(
By.XPATH,'//*[@text="注册/登录" and @index="1"]
').click()
复制代码
Java 版本
driver.findElemnt(By.xpath(
"//*[@text=\"我的\ and @resource-id=\"tab_name\"]")).click();
driver.findElement(By.xpath(
"//*[@text=\"注册/登录\" and @index=\"1"]")).click();
复制代码
(5)XPath 层级定位
我们定位元素的时候可能会设计通过子元素去定位父元素,或者用父元素定位子元素,或者定位兄弟元素,XPath 支持父子关系、兄弟关系元素的查找与定位。示例代码如下(python 版和 Java 版)。
Python 版本
# 通过子元素定位父元素
# 方法一: ..
driver.find_element_by_xpath(
'//*[@text="手机号"]//..').tag.name
# 方法二:parent::*
driver.find_element_by_xpath(
'[@text="手机号"]/parent::*').tag.name
#通过某元素定位其兄弟元素
driver.find_element_by_xpath(
'//*[text="手机号"]/../li'
).tag_name
复制代码
Java 版本
//通过子元素定位父元素
//方法一:..
driver.findElement(By.xpath(
"//*[@text=\"手机号\"]/..")).getTagName();
//方法二 parent::*
driver.findElement(By.xpath(
"[@text=\"手机号\"]/parent::*")).getTagName();
//通过某元素定位其兄弟元素
driver.find_Element(By.xpath(
"//*[@text=\"手机号\"]/../li"
)).getTagName();
复制代码
2.实例案例
(1)场景一
App 应用:雪球 apk。
我们首先使用 UI Automator Viewer 工具对该 App 进行 DOM 分析,然后使用 XPath 对分析到的元素进行定位,如图 5-14 所示的搜索框,我们可以使用元素的多种元素对搜索框进行定位,常用的属性有 text、resource-id、class 和 content-desc 等。
这里推荐使用 resource-id 进行定位,通常情况下,它是 App 页面元素的卫衣属性,用 XPath 编程实现定位的代码如下(Python 版和 Java 版)。
Python 演示代码
driver.find_element(
By.XPATH,'//*[contains(@resource-id,"tv_search")]')
# 或者也可以写成下面这样
driver.find_element(By.ID,'tv_search')
复制代码
Java 演示代码
driver.findElement(By.xpath("//*[contains(@resource-id,
\"tv_search\")]"));
//或者也可以写成下面这样
driver.findElement(By.id("tv_search"));
复制代码
(2)场景二
如图 5-15 所示,获取 “ BABA”所对应的股票价格 “187.11”,可以使用 XPath 父关系来进行元素定位。
定位实现的代码如下(Python 版和 Java 版)
Python 演示代码
curr_price = self.driver.find_element(
MobileBy.XPath,"//*[@text='BABA']/../../..\
//*[@resource-id='com.xueqiu.android:id/current_price']")
复制代码
Java 演示代码
MobileElement curr_price = driver.findElement(
By.xpath("//*[@text=\"BABA\"]/../../..//\
*[@redource-id='com.xueqiu.android:id/current_price']"));
复制代码
3.UIAutomator 定位技巧
UiAutomator 是 Android SDK 自带的一个测试框架,这个测试框架提供了一些列的 API,用这些 API 可以与用 Android 开发的 APP 进行交互,例如,打开菜单、点击、滑动等。当 Appium 的 Caps 参数 uiautomatorName 设置为 UiAutomator2 时,就能够实现计算机端与手机端的 UiAutomator 通信,并且我们可以使用 UiAutomator 执行测试代码。UiAutomator1 是比较旧的版本,如果想测试较旧版本的基于 Android 系统开发的 App(低于 Android 4.4 版本),需要设置 Appium 的 Caps 参数:uiautomationName="UiAutomator1"。
由于 UiAutomator 是 Android SDK 自带的 “工作引擎”,使用这个测试框架进行元素定位,执行速度要比用 XPath 定位元素快很多。但由于 UiAutomator 的用法比较特殊,调试起来相对麻烦,如果测试脚本中的定位语句编写不当,脚本编辑器也不会给出任何错误提示信息,只能在运行的时候检验测试脚本的对错。
下面就单独介绍一下基于 UiAutomator 定位元素的方法,基本语法如下(Python 版和 Java 版)。
Python 版本
driver.find_elemnet_by_android_uiautomator()
复制代码
Java 版本
driver.findElement(MobileBy.AndroidUIAutmator());
复制代码
常用的 UiAutomator 方法如下:
UiSelenium() #实现元素定位
UiScrollable() #实现滚动查找元素
复制代码
(1)通过 text 定位
UiSelector()的用法与 XPath 类似,可以通过元素的 text 属性来定位元素。Uiselector 的语法格式如下:
new UiSelector().text("text文本")
复制代码
UiSelector 也适用模糊查询的方式来定位元素。
UiSelector 的演示代码如下(Python 版和 Java 版)。
Python 版本
driver.find_element_by_android_uiautomator(
'new UiSelector().textContains("手机")').click()
复制代码
Java 版本
driver.findElementByAndroidUIAutomator(\
"new UiSelector().textContains(\"手机\")").click();
复制代码
(2)通过 resourceId 定位
UiAutomator 同样也能用 ID 定位元素,格式为 new UiSelector().resourceId("resource-id 属性"),示例代码如下(Python 版和 Java 版本)。
Python 版本
driver.find_element_by_android_uiautomator(
'new UiSelector().resourceId("rl_login_phone")').click()
复制代码
Java 版本
driver.findElementByAndroidUIAutomator("new UiSelector().\
resourceId(\"rl_login_phone\")").click();
复制代码
(3)通过 className 定位
App 页面上元素的 class 属性一般不唯一,此时可以根据下标对元素定位,格式 new UiSelector().className("className"),一般使用 find_elements 完成元素定位,示例代码如下(Python 版和 Java 版)。
Python 版本
driver.find_elements_by_android_uiautomator(
'new UiSelector().\
className("android.widget.TestView"))'[5].click()
复制代码
Java 版本
driver.findElementsByAndroidUIAtor("new UiSelector().\
className(\"android.widget.TestView\")")[5].click();
复制代码
(4)通过 description 定位
description 也支持 content-desc 定位方式,格式为 new UiSelector().description("content-des"属性),示例代码如下(Python 版和 Java 版)。
Python 版本
driver.find_element_by_android_uiautomator(
'new UiSelector().description("搜索")').click()
复制代码
Java 版本
driver.findElementByAndroidUIAutomator("new \
UiSelector().description(\"搜索\")").click();
复制代码
(5)组合定位方式
UiAutomator 也支持属性组合定位元素,示例代码如下(Python 版和 Java 版)。
Python 版本
driver.find_element_by_android_uiautomator(
'new UiSelector().resourceId(\
"com.xueqiu.android:id/tv_login_phone").text("手机号")').click();
复制代码
Java 版本
driver.findElementByAndroidUIAutomator("new UiSelector().resourceId(\
\"com.xueqiu.android:id/tv_login_phone\").text(\"手机号\")").click();
复制代码
(6)滚动查找元素
UiAutomator 使用 UiScrollable()方法可以滚动查找指定的某个元素,示例代码如下(Python 版和 Java 版)。
driver.find_element_by_android_uiautomator(
'new UiScrollable(new UiSelector().scrollable(true\
.instance(0)).scrollIntoView(new UiSelector()\
.text("我的").instance(0);').)click()
复制代码
Java 版本
driver.findElementByAndroidUIAutomator(\
"new UIScrollable(new UiSelector().scrollable(ture)\
.instance(0)).scrllIntoView(new UiSelector().\
text(\"我的\").instance(0));").click();
复制代码
上面的测试代码作用是,在当前的页面滚动查找 text 属性值是 “我的” 这个元素,找到之后执行点击操作。
4.css selector 元素定位
Appium Server 从 1.19.0 版本开始,增加了用 css selector 方式进行元素定位的支持,即 appiumuiautomator2-driver 会将 css selector 定位方式转化成 UiAutomator 定位方式。
注意:Appium Inspector 中暂时没有添加这种定位当时。
由于 UiSelector()的表达式用的是 Java 格式的语法,因此编写定位元素的表达式很复杂,且在用工具编写代码时(如 Pycharm、VSCode、IntelliJ IDEA 等工具),若代码编写错误也不会有任何提示信息。只能是代码运行时才能发现其中表达式的错误。css selector 的语法会自动转成 UiAutomator 的语法结构,这种官方提供的原生的定位元素的方式,定位速度更快一些。
(1)ID 定位
css selector 可以用 ID 进行元素定位。飞马如下(Python 版和 Java 版)(#igk 表示 css selector 定位符)。
Python 版本
driver.find_element_by_css_selector('#igt')
driver.find_element_by_id_('android:id/igk')
复制代码
Java 版本
driver.findElementByCssSelector("#igk").click();
driver.findElementById("android:id/igk").click();
复制代码
(2)class name 定位
css selector 使用 class name 进行元素定位的代码如下(Python 版和 Java 版)(css selector 的定位符为.android.widget.ImageView)。
Python 版本
driver.find_element_by_css_selector('.android.widget.ImageView')
driver.find_element_by_class_name("android.widget.ImageView")
复制代码
Java 版本
driver.findElementByCssSelector(".android.widget.ImageView");
driver.findElementByClassName("android.widget.ImageView");
复制代码
(3)text 定位
css selector 使用 text 进行元素定位的代码如下(Python 版和 Java 版)(css selector 的定位符为“*[text=‘工作台’]”)。
Python 版本
driver.find_element_by_css_selector("*[text='工作台']")
复制代码
注:对应 XPath 定位器的代码时 driver.find_element_by_xpath("//*[@text='工作台']")。
Java 版本
driver.findElementByCssSelector("*[text=\"工作台\"]");
复制代码
注:对应 XPath 定位器的代码是 driver.findElementByXPath("//*[@text= \ "工作台"]").
(4)description 定位
css selector 使用 description 进行元素定位的代码如下(Python 版和 Java 版)(css selector 的定位符为*[description="ContentDescription"])。
Python 版本
driver.find_element_by_scc_selector('*[description="ContentDescription"]')
复制代码
对应 accessibility id 定位器的代码如下:
driver.find_element_by_assessibility_id("ContentDescription")
复制代码
Java 版本
driver.findElementByCssSelector("*[description=\"ContentDescription\"]");
复制代码
对应 accessibility id 的定位器代码如下:
driver.findElementByAccessibilityId("ContentDescription");
复制代码
搜索微信公众号:TestingStudio 霍格沃兹的干货都很硬核
评论