强制等待与隐式等待
为什么要添加等待?
- 避免页面未渲染完成后操作,导致的报错
直接等待
- 解决方案:在报错的元素操作之前添加等待
- 原理:强制等待,线程休眠一定时间
time.sleep(3)
from appium import webdriver
import time
desired_caps={}
desired_caps['platformName']='Android'
desired_caps['platformVersion']='6.0'
desired_caps['deviceName']='emulator-5554'
desired_caps['appPackage']='com.xueqiu.android'
desired_caps['appActivity']='com.xueqiu.android.common.MainActivity'
desired_caps['noReset'] = "true"
driver = webdriver.Remote("http://127.0.0.1:4723/wd/hub",desired_caps)
time.sleep(3)
driver.find_element(AppiumBy.ID, \
"com.xueqiu.android:id/tv_search").click()
time.sleep(3)
driver.find_element(AppiumBy.ID, \
"com.xueqiu.android:id/search_input_text").send_keys("alibaba")
driver.find_element(AppiumBy.ID,\
"com.xueqiu.android:id/code").click()
driver.quit()
隐式等待
- 问题:难以确定元素加载的具体等待时间。
- 解决方案:针对于寻找元素的这个动作,使用隐式等待添加配置。
- 原理:隐式等待是一种全局的等待方式,设置一个等待时间,轮询查找(默认 0.5 秒)元素是否出现,如果没出现就抛出异常
#设置一个等待时间,轮询查找(默认0.5秒)元素是否出现,如果没出现就抛出异常
driver.implicitly_wait(3)
隐式等待无法解决的问题
- 元素可以找到,使用点击等操作,出现报错
- 原因:
- 页面元素加载是异步加载过程,通常 xml 会先加载完成,相应的元素属性后加载
- 元素存在与否是由 xml 决定,元素的交互是由属性决定
- 隐式等待只关注元素能不能找到,不关注元素能否点击或者进行其他的交互
- 解决方案:使用显式等待
显式等待基本使用(初级)
- 示例:
WebDriverWait(driver实例, 最长等待时间, 轮询时间).until(结束条件)
- 原理:在最长等待时间内,轮询,是否满足结束条件
- 注意:在初级时期,先关注使用
WebDriverWait(driver, 10).until(
expected_conditions.element_to_be_clickable(
(AppiumBy.ID, 'com.xueqiu.android:id/code')))
driver.find_element(AppiumBy.ID,"com.xueqiu.android:id/code").click()
常见的交互方法
元素的常用方法
- 点击方法
element.click()
- 输入操作
element.send_keys('appium')
- 设置元素的值
element.set_value('appium')
- 清除操作
element.clear()
- 是否可见
element.is_displayed()
返回 True/False - 是否可用
element.is_enabled()
返回 True/False - 是否被选中
element.is_selected()
返回 True/False - 获取属性值
get_attribute(name)
元素的常用方法
-
get_attribute()
方法能获取的属性,元素的属性几乎都能获取到,属性名称和 uiautomatorviewer 里面的一致 - get_attribute() 可以获取的属性
- resource-id/resourceld 返回 resource-id(API=>18 支持)
- text 返回 text
- class 返回 class(API=>18 支持)
- content-desc/contentDescription 返回 content-desc 属性
- checkable,checked,clickable,enabled,focusable,focused,{long-clickable,longClickable), package, password,scrollable,selection-start,selection-end,selected,bounds,displayed,contentSize 返回 true or false
元素常用属性
- 获取元素文本
- 格式:element.text
- 获取元素坐标
- 格式:element.location
- 结果:
{'y': 19,'x: 498}
- 获取元素尺寸(高和宽)
- 格式:element.size
- 结果:
{'width':500,'height':22)
定位策略
选择定位器通用原则
- 与研发约定的属性优先
- web 推荐 class
- android 推荐 content-description
- ios 推荐 label
- 身份属性 id,name(web 定位)
- 组合定位 xpath,css
- 其它定位
元素定位不到
原因 解决办法
定位不正确 在定位工具中先测试定位表达式是否正确
存在动态 ID 定位方式使用 css 或者 xpath 的相对定位
页面还没有加载完成 添加死等验证,使用显示等待或隐式等待进行优化
页面有 iframe 切换到 iframe 后定位
页面切换 window 切换到对应窗口后定位
要定位元素为隐藏元素 使用 js 操作该元素
混合定位的应用场景
- 场景:
- 属性动态变化(id,text)
- 重复元素属性(id,text,class)
- 解决:
- 根据相对位置关系进行定位(css、xpath)(父级,子级,兄弟,索引)
- 使用 find_elements 遍历查找
使用等待机制的场景
- 场景
- 控件动态出现
- 控件出现特定特征
- 解决
- 元素定位结合隐式等待与显式等待
Web 弹框定位
- 场景
- web 页面 alert 弹框
- 解决:
- web 需要使用
driver.switchTo().alert()
处理
- web 需要使用
App toast 提示框定位
- 场景
- app toast 提示框
- 解决:
- 使用 driver.page_source 拿到页面布局结构文件,分析 toast/弹框组件的标签内容,
- 然后通过 id/text/class 等属性,使用 xpath 完成元素定位
- 结合 隐式等待
下拉框/日期控件定位
- 场景:
-
<input>
标签组合的下拉框无法定位 -
<input>
标签组合的日期控件无法定位
-
- 解决:
- 面对这些元素,我们可以引入 JS 注入技术来解决问题。
文件上传定位
- 场景:
- input 标签文件上传
- 解决:
- input 标签直接使用 send_keys()方法
Appium Server 安装(Windows)
安装 Node.js
// 查看源
npm config get registry//更换源
npm config set registry https://registry.npm.taobao.org
版本查看
node -v
npm -v
安装 Appium Server
// 安装最新版本的appium server
npm install -g appium
- 注意 -g 参数一定要有,不能省,代表全局
- 安装某个特定的版本
npm install -g appium@1.19.1
安装环境检测工具
- 运行环境检测工具 appium-doctor
# 安装检测工具 appium-doctor
npm install -g appium-doctor
环境检测
- 执行检测命令
appium-doctor
运行 Appium 服务
// 帮助文档
appium --help// 运行 appium 服务
appium
Capabilities 进阶
deviceName
- 只是设备的名字,别名
- 随便起
- 不能锁定唯一一个设备
uid
- 多设备选择的时候,要指定 uid
- 默认读取设备列表的第一个设备
- 设备列表获取
adb devices
newCommandTimeout
- appium 程序应等待来自客户端的新命令多长时间
- 超时后==会话删除==
- 默认
60
秒 - 设置为 0 禁用
autoGrantPermissions
- 授予启动的应用程序某些权限
PRINT_PAGE_SOURCE_ON_FIND_FAILURE
- 默认为
false
- 发生任何错误,强制服务器将实际的 XML 页面源转储到日志中.
测试策略
noReset
- 默认为
false
- 安卓
true
- 不停止应用程序
- 不清除应用程序数据
- 不卸载 apk
fullReset
- 默认为
false
。true
:新会话之前完全卸载被测应用程序 - 安卓
- 在会话开始之前(appium 启动 app)和测试之后停止应用程序
- 清除应用程序数据并卸载 apk
dontStopAppOnReset
- 默认为
false
。 - 不希望应用程序在运行时重新启动,设置为
true
#打开的app退出后重新启动
adb shell am start -S 包名/activity名
#打开的app不需要退出,直接使用当前页面
adb shell am start 包名/activity名