我们必须告诉Appium怎样去定位元素,用来模拟用户动作,或者查看元素的属性和状态,以便我们可以执行检查。例如,我们在淘宝上购物时搜索一个产品,首先需要找到搜索框和搜索按钮,
接着通过键盘输入要购买物品的关键字,最后点击搜索按钮,提交搜索请求。
正如上述人工的操作步骤一样,我们也希望Appium能模拟我们的动作,然而,Appium并不能理解在搜索框中输入关键或者单价搜索按钮这样的图形化操作。所以需要我们程序化的去告诉Appium如何
定位搜索框和搜索按钮,从而模拟输入和点击的动作。
Appium提供多种元素的定位方法,如:id、name、class、相对定位、Xpath定位、List定位、Uiautomator定位等等……
接下来我们对这几种方法进行一一讲解,并且提供相应的例子。
- PS:元素定位工具我使用的UI Automatorviewer,在这里就不在介绍该工具如何使用了,可以访问我的另一篇博客:https://blog.csdn.net/px551/article/details/104558672
from appium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
#参数配置
desired_caps={
'platformName':'Android',
'platformVersion':'6.0.1',
'deviceName':'127.0.0.1:21305',
'appPackage':'com.baidu.homework',
'appActivity':'com.baidu.homework.activity.index.IndexActivity',
'automationName': 'Appium',
'noReset':True,
}
driver=webdriver.Remote('http://127.0.0.1:4723/wd/hub',desired_caps)
ID属性:本机元素标识符。也就是通过UI Automatorviewer工具查看的resource-id属性;IOS 的 Name。
如上图所示,就可以通过ID属性能定位到用户名的输入框:
driver.find_element_by_id("com.baidu.homework:id/passport_phone_number_input_edit")
根据ID属性来举一个登陆作业帮APP的例子:
def zuoyebang_login(un="zuoyebang",pw="zuoyebang!test"):
#点击未登录按钮
driver.find_element_by_id("com.baidu.homework:id/rl_login_guide_layout").click()
#点击密码登陆
driver.find_element_by_id("com.baidu.homework:id/sll_password").click()
#输入用户名
driver.find_element_by_id("com.baidu.homework:id/passport_phone_number_input_edit").clear()
driver.find_element_by_id("com.baidu.homework:id/passport_phone_number_input_edit").send_keys(un)
#输入密码
driver.find_element_by_id("com.baidu.homework:id/passport_password_input_view").clear()
driver.find_element_by_id("com.baidu.homework:id/passport_password_input_view").send_keys(pw)
#登陆
driver.find_element_by_id("com.baidu.homework:id/tv_phone_enter").click()
#调用登陆方法
zuoyebang_login()
name定位就是通过UI Automator View工具查看的text属性,若text为空就不能以此方法定位了。
**appium1.5以下老的版本是可以通过name定位的,新版本从1.5以后都不支持name定位了**
driver.find_element_by_name("输入手机号码")
由于此方法已经被1.5以后的版本抛弃,所以我也不在这里举例了,大家了解一下这个就可以了。
**CLASS_NAME属性:**Android, UIAutomator2 类的全名; IOS, 以XCUIElementType 开头的XCUI 元素全名。
classname定位是根据元素类型来进行定位,但是实际情况中很多元素的classname都是相同的,有多个只能定位到第一个。
格式:
driver.find_element_by_class_name("这里是class属性")
如上图所示,上面的三种登陆方式的classname都是一样的,所以只能定位到第一个登陆方式“微信”。这种情况我们可以按照find_elements 定位多个元素,下面会讲到这种定位方式。
通过附加给定元素的辅助功能ID或标签定位元素,如 Android 的 content-desc属性(就是通过UI Automator工具查看的content-desc属性), iOS 的 accessibility-id 属性。
如上图的定位,看到content-desc属性有值,就可以通过accessibility_id来定位了
格式:driver.find_element_by_accessibility_id(“content-desc的值”)
driver.find_element_by_accessibility_id(“xxx”)
Uiautomator元素定位是Android系统原生支持的定位方式,虽然与xpath相似,但是比它更好用,且支持元素的全部属性定位。定位原理是通过Android自带的Android Uiautomator的类库去查找元素。Appium元素定位方法其实也是基于Uiautomator来进行封装的
常用的定位方式有:
# 根据text定位
self.driver.find_element_by_android_uiautomator('new UiSelector().text("text属性")')
# 根据id定位
self.driver.find_element_by_android_uiautomator('new UiSelector().resourceId("id属性")')
# 根据class定位
self.driver.find_element_by_android_uiautomator('new UiSelector().className("class属性")')
还是以上面的登陆为例
def login():
#点击未登录按钮
driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.baidu.homework:id/rl_login_guide_layout")').click()
#点击密码登陆
driver.find_element_by_android_uiautomator('new UiSelector().text("密码")').click()
#输入用户名
driver.find_element_by_android_uiautomator('new UiSelector().text("输入手机号码")').clear()
driver.find_element_by_android_uiautomator('new UiSelector().text("输入手机号码")').send_keys('phonenumber')
#输入密码
driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.baidu.homework:id/passport_password_input_view")').clear()
driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.baidu.homework:id/passport_password_input_view")').send_keys('password')
#登陆
driver.find_element_by_android_uiautomator('new UiSelector().resourceId("com.baidu.homework:id/tv_phone_enter")').click()
if __name__ == '__main__':
login()
注意:如果用class定位,那么一个页面上有相同的class属性,所以这个只能定位到第一个属性元素
相对定位是先找到该元素对应的父元素节点,然后基于父元素进行定位。
此种情况是如果该元素在同一页面上有多个相同的属性,而父元素的属性是唯一的,那么先定位到它的父元素,再定位子元素。
格式:
#先定位到父级元素
ele=driver.find_element_by_id("父元素resourceid")
#再定位该元素
ele.find_element_by_class_name("classname属性").click()
List定位首先是使用find_elements_by_XX获取一组相同属性的元素,然后使用数组下标来区分标记不同元素进行相关操作,下标从0开始。
如按照class属性来定位:
#find_elements_by_class_name("class属性")[6]定位以密码方式登陆
#这里的登陆方式class属性都是一样的,所以先定位到classname,存放到一个列表里面,然后根据下标来确定
ele=driver.find_elements_by_class_name("android.widget.TextView")
ele.[6].click()
xpath定位是一种路径定位方式,主要是依赖于元素绝对路径或者相关属性来定位,但是绝对路径xpath执行效率比较低,一般使用比较少,通常使用xpath相对路径和属性定位。
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。 |
/ | 从根节点选 |
. | 选取当前节点 |
@ | 选取属性 |
* | 匹配任何元素节点 |
@* | 匹配任何属性节点 |
node() | 匹配任何类型的节点 |
# 没有contains时,属性和值之间用=
driver.find_element_by_xpath('//*[@text="请输入手机号"]').send_keys('phonenumber')
driver.find_element_by_xpath('//*[@resource-id="com.jgw.csca:id/et_pwd"]').send_keys('123456')
#多个属性值之间
driver.find_element_by_xpath("//*[@class='android.widget.RelativeLayout' and @index='3']").click()
# 有contains时,属性和值之间用,中间可以用and
driver.find_element_by_xpath('//*[contains(@text,"登录") and contains(@index,"5")]').click()
#//title[@lang='eng']
driver.find_element_by_xpath('//android.widget.EditText[@text="输入手机号码"]').send_keys('phonenumber')
#兄弟节点查找,..表示选取当前节点的父节点,先通过弟弟节点找到父元素,再通过父元素找到哥哥元素
driver.find_element_by_xpath('//*[@resource-id="com.jgw.csca:id/et_pwd"]/../android.widget.EditText')
只适用于webview的html页面,继承自webdriver,与pc版本的UI测试一致
driver.find_element_by_css_selector()