通常使用定位工具定位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");