APP自动化测试-3. Appium元素定位与等待
文章目录
前言
主要介绍一下Appium初始化driver 的配置项及常用的元素定位方法,还有定位工具uiautomatorviewer的简单适用
一、Appium Desired Capabilities简介
1. Appium Desired Capabilities通用参数
- automationName:使用的自动化引擎,通常保持默认即可
- platformName:运行的平台,如iOS, Android, or FirefoxOS
- platformVersion:系统版本
- deviceName:被测的设备名称
- app:.apk或.ipa文件的绝对路径或下载地址,首次会进行应用的安装
- browserName:被测试浏览器名称,如Safari、Chrome、Browser等
- newCommandTimeout:超时时间
- udid:测试多台设备时,需要用来置顶设备的ID
- noReset:默认为False,设置为True后,会保留session信息,比如登录信息,操作信息等
- FullReset:与noReset相反
2. Appium Desired Capabilities Android常用参数
- appActivity:应用的页面
- appPackage:应用的包名
- unicodeKeyboard:默认为false,设置为True可以进行中文输入
- resetKeyboard:配合unicodeKeyboard使用,在测试完成后将键盘恢复为原始状态
- skipDeviceInitialization:跳过安装、权限设置等操作
- dontStopAppOnReset:执行测试时如果目标应用已存在,则无需重新打开应用
3. Appium Desired Capabilities IOS常用参数
- bundleId:应用包名
以简书为例,简单示例:
from time import sleep
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
desired_caps = {
"platformName": "android",
"appium:deviceName": "b1f37e8e",
"appium:appPackage": "com.jianshu.haruki",
"appium:appActivity": "com.baiji.jianshu.ui.splash.SplashScreenActivity",
# 添加noReset后,会记录该应用之前的操作
"noReset": True,
# 设置dontStopAppOnReset后,如果应用已打开,则不会关闭应用重新打开
"dontStopAppOnReset": True,
# 跳过安装及权限设置操作,提升执行速度
"skipDeviceInitialization": True
}
driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
# 设置隐式等待
driver.implicitly_wait(3)
driver.find_element(by=AppiumBy.ID, value="com.jianshu.haruki:id/et_account").send_keys("13000000000")
sleep(3)
driver.quit()
二、Appium常用定位方法
1. ID定位
id定位基本可selenium定位差不多,调用find_element()方法,传入定位方法和resource-id的值即可
id的值可以通过Appium inspector查看:
代码示例:
# by为定位方法,ID即为AppiumBy.ID,value为ID的值
driver.find_element(by=AppiumBy.ID, value="com.jianshu.haruki:id/et_account")
注:id和resource-id的值通常是一致的
2. XPATH定位
(1)定义:
XPath即为XML路径语言(XML Path Language),它是一种用来确定XML文档中某部分位置的语言。
XPath基于XML的树状结构,提供在数据结构树中找寻节点的能力。起初XPath的提出的初衷是将其作为一个通用的、介于XPointer与XSL间的语法模型。但是XPath很快地被开发者采用来当作小型查询语言。
(2)语法:
选取节点 XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。
常用表达式:
表达式 | 描述 |
---|---|
nodename | 选取此节点的所有子节点 |
/ | 从根节点选取,路径中/代表当前节点选取元素 |
// | 从匹配选择的当前节点选择文档中的节点,选择当前节点的所有子节点(子节点不限制层级) |
. | 选取当前节点 |
… | 选取当前节点的父节点 |
@ | 选取属性 |
常用路径表达式示例:
路径表达式 | 说明 |
---|---|
/android.widget.RelativeLayout/android.widget.TextView[1] | 选取属于android.widget.RelativeLayout子元素的第一个android.widget.TextView元素 |
/android.widget.RelativeLayout/android.widget.TextView[last()] | 选取属于android.widget.RelativeLayout子元素的最后一个android.widget.TextView元素 |
/android.widget.RelativeLayout/android.widget.TextView[last()-1] | 选取属于android.widget.RelativeLayout子元素的倒数第二个android.widget.TextView元素 |
//android.widget.TextView[@text=‘登录’] | 选取所有的android.widget.TextView元素,且这些元素拥有值为:登录 的text属性 |
3. ACCESSIBILITY ID定位
uiautomatorviewer界面上没有这个字段,这个对应的字段是content-desc
driver.find_element(by=AppiumBy.ACCESSIBILITY_ID, value='test').click()
4. CSS SELECTOR定位
基本与web端的定位是一样的规则,不过原生应用并不支持css selector,只有app中嵌套的网页可以使用该种定位方式
选择器 | 例子 | 描述 |
---|---|---|
.class | .intor | 选择class="intro"的所有元素 |
#id | #firstname | 选择id="firstname"的所有元素 |
element | * | 选择所有元素 |
element,element | div,p | 选择所有div元素和p元素,同时选择2个元素 |
element element | div p | 选择div元素内部的所有p元素,无视层级,相当于xpath的// |
element>element | div>p | 选择div元素所有的子元素p,注意是子元素,相当于xpath的/ |
element+element | div+p | 选择在div元素之后的所有p元素,同一个层级 |
[attribute] | [target] | 选择带有target属性的所有元素 |
[attribut=value] | [target=_blank] | 选择target="_blank"的所有元素 |
:nth-child(n) | p:nth-child(2) | 选择属于父元素的第二个字元素的每个p元素 |
element1~element2 | p~ul | 选择前面有p元素的每个ul元素 |
三、Appium常用等待
1. 强制等待
使用sleep进行强制等待,不建议使用,时长无法确认,成功也无法确保
2. 隐式等待
使用implicitly_wait()方法进行隐式等待,隐式等待时全局的,适用于大部分场景
3. 显式等待
显式等待可以对个别难以获取的元素进行单独定义,定义完成后可以确保元素被查找到或可操作为止。
四、元素定位工具(android)
uiautomatorviewer相对于Appium inspector更轻量一些,可以直接命令使用
1. uiautomatorviewer启动
通常Android sdk安装完成后,uiautomatorviewer已经可以正常使用,在terminal窗口,直接执行uiautomatorviewer即可,执行完成后弹出手机页面:
- 左上角第一个按钮打开,打开保存的页面元素文件
- 第二个按钮同步手机页面,并在右侧展示当前页面的元素
- 第三个按钮,压缩当前页面的元素,只显示主要的元素
- 第四个按钮保存当前元素页面,包含一个截图和元素文件
2. uiautomatorviewer使用
uiautomatorviewer窗口左侧栏中选择需要定位的元素,右侧dom树查找到对应的元素,根据对应信息编写定位的表达式即可
3. uiautomatorviewer异常处理
问题现象:mac启动uiautomatorviewer后,uiautomatorviewer窗口不显示操作按钮,类似卡住未响应的状态
问题原因:兼容性有问题
解决方案:
- 下载swt.jar工具包,下载地址:https://archive.eclipse.org/eclipse/downloads/drops4/R-4.20-202106111600/swt-4.20-cocoa-macosx-x86_64.zip
- 解压文件,重命名文件夹中的swt.jar为swt2.jar
- 拷贝swt2.jar文件致%adbpath//tools/lib/x86_64下(目录下应该有个swt.jar文件)
- 启动uiautomatorviewer,最大化uiautomatorviewer窗口,点击第二个按钮即可正常使用
五、uiautomator定位
uiautomator定位是android的工作引擎,速度快,缺点是表达式比较复杂,所以还是建议使用常规定位,这里简单做一下介绍
语法:driver.find_element_by_android_uiautomator(“uiautomator表达式”).click()
1. uiautomator基本定位
- resourceid:通过resourceid定位,表达式:new UiSelector().resourceId(“id”)
- classname:通过classname定位,表达式:new UiSelector().className(“className”)
- content-desc:通过content-desc定位,表达式:new UiSelector().description(“content-desc值”)
- text:通过text定位,表达式:new UiSelector().text(“text文本”)
- textContains:通过包含某个文本来定位,表达式:new UiSelector().textContains(“包含的文本”)
- textStartsWith:通过某个文本开头来定位,表达式:new UiSelector().textStartsWith(“开头的文本”)
- textMatches:通过正则表达式来定位,表达式:new UiSelector().textMatches(“正则表达式”)
from time import sleep
import pytest as pytest
from appium import webdriver
from appium.webdriver.common.appiumby import AppiumBy
class TestTouch:
def setup(self):
desired_caps = {
"platformName": "android",
"appium:deviceName": "b1f37e8e",
"appium:appPackage": "com.jianshu.haruki",
"appium:appActivity": "com.baiji.jianshu.ui.splash.SplashScreenActivity",
# 添加noReset后,会记录该应用之前的操作
"noReset": True,
# 设置dontStopAppOnReset后,如果应用已打开,则不会关闭应用重新打开
# "dontStopAppOnReset": True,
# 跳过安装及权限设置操作,提升执行速度
"skipDeviceInitialization": True
}
self.driver = webdriver.Remote('http://localhost:4723/wd/hub', desired_caps)
# 设置隐式等待
self.driver.implicitly_wait(3)
def teardown(self):
self.driver.quit()
def test_automator(self):
# uiautomator resource id定位
self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().resourceId("com.jianshu.haruki:id/iv_mine")').click()
self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("点击登录")').click()
self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, 'new UiSelector().text("账号密码登录")').click()
self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().resourceId("com.jianshu.haruki:id/iv_clear_account")').click()
self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().resourceId("com.jianshu.haruki:id/et_account")').send_keys(
"13000000000")
self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().resourceId("com.jianshu.haruki:id/et_verification_code_or_password")').send_keys(
"Aa123456")
self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().className("android.widget.CheckBox")').click()
self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
'new UiSelector().resourceId("com.jianshu.haruki:id/tv_login")').click()
sleep(5)
if __name__ == 'main':
pytest.main()
2. uiautomator组合定位
uiautomator可以进行组合定位,提高定位的准确性
- 多个属性组合定位:
id_text = 'new UiSelector().resourceId("com.jianshu.haruki:id/tv_login").text("登录")'
self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, id_text).click()
- 父子节点组合定位:
父元素表达式后使用childSelector方法,方法中输入子元素的表达式
id_text = 'new UiSelector().className("android.widget.RelativeLayout").childSelector(resourceId("com.jianshu.haruki:id/tv_login"))'
self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, id_text).click()
- 兄弟节点定位:
兄弟节点表达式后使用fromParent()方法,方法中输入目标元素的表达式
id_text = 'new UiSelector().className("android.widget.RelativeLayout").fromParent(resourceId("com.jianshu.haruki:id/tv_login"))'
self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR, id_text).click()
注:组合定位可以在表达式后面一直添加元素的属性,确保定位的准确性
3. 滚动查找定位
页面元素当前也没有时,进行滚动查找元素
表达式:new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().text(“测试”).instance(0));
代码示例:
def test_scroll(self):
self.driver.find_element(AppiumBy.ANDROID_UIAUTOMATOR,
'new UiScrollable(new UiSelector().scrollable(true).instance(0)).scrollIntoView(new UiSelector().textContains("测试").instance(0));').click()
总结
- appium中初始化driver,常用的配置项需要熟悉
- 定位基本和selenium中是一致的,但是需要注意的是有些定位仅适用于app中嵌套的网页,不适用原生的应用
- uiautomatorviewer使用时可能会有兼容问题,需要处理