一、定位元素工具weditor
iOS定位工具这里推荐使用weditor,安卓同样可以使用这个工具进行元素连接,appium inspect试过很多次start session连接之后总是提示连接超时,遂放弃使用该工具
二、启动定位工具
参考Android安装环境方法, 使用以下命令安装weditor
pip3 install -U weditor
确保本机已经安装iTunes,并且当前的iOS设备已经连接上且识别到(参考另一篇博文使用tidevice list命令查看)安装完成后使用以下命令开启
python -m weditor
等待网页自动开启,默认显示是这样的
模块选择iOS,默认连接是本地8100接口,点击Connect,发现会出现以下错误
这是由于本机和iOS设备通信失败,此时使用tidevice程序发起和WebDriverAgent之间的通信,保持手机设备可以识别(非锁屏状态),然后运行以下命令
tidevice -u [设备uuid] wdaproxy -B [webdriveragent bundleid] --port 8100
[I 220611 13:38:23 _wdaproxy:125] [设备uuid] WDA check every 30.0 seconds
[D 220611 13:38:23 _wdaproxy:131] [设备uuid] launch WDA
[I 220611 13:38:24 _device:969] BundleID: [webdriveragent bundleid]
[I 220611 13:38:24 _device:985] ProductVersion: 12.5.5
[I 220611 13:38:24 _device:986] DeviceIdentifier: [设备uuid]
[I 220611 13:38:24 _device:822] SignIdentity: 'Apple Development: xxxxxxxxx@qq.com (2PQLXK79WG)'
[I 220611 13:38:24 _device:828] CFBundleExecutable: WebDriverAgentRunner-Runner
[I 220611 13:38:24 _device:859] AppContainer: /private/var/mobile/Containers/Data/Application/A26DF5B4-203E-4035-9D81-10CC86564A1A
[I 220611 13:38:24 _device:899] Launch '[webdriveragent bundleid]' pid: 986
[I 220611 13:38:25 _device:1035] Test runner ready detected
[I 220611 13:38:25 _device:1027] Start execute test plan with IDE version: 29
[I 220611 13:38:26 _device:935] WebDriverAgent start successfully
等待WDA启动之后,再次点击Connect发现weditor可以正常同步iOS设备,Connect右侧的绿色树叶图标代表已经成功连接
接下来就可以打开需要测试的App进行元素定位了
三、WDA初始化
全局配置
import wda
wda.DEBUG = False # default False
wda.HTTP_TIMEOUT = 180.0 # default 180 seconds
wda.DEVICE_WAIT_TIMEOUT = 180.0
创建一个客户端
import wda
wda.DEBUG = False # default False
wda.HTTP_TIMEOUT = 180.0 # default 180 seconds
wda.DEVICE_WAIT_TIMEOUT = 180.0
client = wda.Client('http://localhost:8100') # url参数为默认为: http://localhost:8100, 可以修改为其他端口或者ip
设备操作
接上面创建客户端的代码,执行前确认WDA服务正常
打开功能菜单或者App
client.session("appname") # app_name可以通过tidevice applist进行查看
# 这里我们以打开iOS设备的设置菜单为例
client.session('com.apple.Preferences')
# 还可以通过USBClient连接
client = wda.USBClient() # 仅连接一个设备可以不传参数
client = wda.USBClient("设备udid", port=8100) # 指定设备 udid 和WDA 端口号
client = wda.Client("http+usbmux://{udid}:8100".format(udid="udid")) # 通过DEVICE_URL访问
client = wda.USBClient("udid", port=8100, wda_bundle_id="webdriveragent bundleid") # 引入 wda_bundle_id 参数
针对USBClient(),初始化连接设备时不需要事先使用tidevice命令启动WDA,wda.Client()会自动启动WDA应用,确认iOS设备的设置菜单被打开
关闭功能菜单或者App
client.close() # 关闭
# 还可以使用app_terminate
client.app_terminate('com.apple.Preferences') # 停止App命令
设备控制
# 回到手机主页面
client.home()
client.press("home")
# 增大降低音量
client.press("volumeUp")
client.press_duration("volumeUp", 1) # 长按1s音量上键
client.press("volumeDown")
client.press_duration("volumeDown", 1) # 长按1s音量下键
# 锁屏
client.locked() # 返回锁定状态True/False
client.lock() # 锁屏
client.unlock() # 解锁
获取设备应用信息
# 查看设备状态信息
client.status()
# 获取应用信息
client.app_current()
# 获取设备信息
client.device_info()
# 获取电量信息
client.battery_info()
# 获取分辨率
client.window_size()
四 、元素定位
通过属性值进行定位
id
client(id='element_id')
className
client(className="element_className")
name
client(name="element_name")
value
client(value="element_value")
label
client(label="element_label")
client(labelContains="element_label_content")
组合定位
可以进行多个属性组合进行定位,例如:
client(className="element_className", name="element_name")
子元素定位
# 子元素定位
client(className='element_className').child(name='child_element_name').exists
XPath
XPath是XML路径语言,是一种查询语言,使用路径表达式浏览XML文档中的元素和属性。XPath标准语法如下:
XPath=//tagname[@attribute='value']
// : 选择当前节点
tagname: 节点标签名
@: 选择属性
Attribute: 节点属性名
Value: 属性值
示例如下:
client(xpath='//*[@name="element_name"]')
# 或者
client.xpath('//*[@name="element_name"]')
Predicate定位
Predicate定位是iOS原生支持的定位方式,定位速度比较快,它可以通过使用多个匹配条件来准确定位某一个或某一组元素
基本比较
符号 | 说明 | 示例 |
= , == | 等于 | name == “通知” |
> | 大于 | name > 10 |
< | 小于 | name < 10 |
>= , => | 大于等于 | name >= 10 |
<= , =< | 小于等于 | name <= 10 |
!= , <> | 不等于 | name != “通知” |
集合操作
符号 | 说明 | 示例 |
ANY , SOME | 满足表达式的任意元素 | ANY children.age < 18 |
ALL | 满足表达式的所有元素 | ALL children.age < 18 |
NONE | 不包含满足表达式的任意元素 | NONE children.age < 18 |
IN | 元素在集合中 | name IN { ‘Ben’, ‘Melissa’, ‘Nick’ } |
BETWEEN | 位于某个范围 | 1 BETWEEN { 0 , 33 } |
array[index] | 数组array中指定索引的元素 | |
array[FIRST] | 数组中的第一个元素 | |
array[LAST] | 数组中的最后一个元素 | |
array[SIZE] | 指定数组大小 |
布尔值
符号 | 说明 | 示例 |
TRUEPREDICATE | TRUE | |
FALSEPREDICATE | FALSE |
逻辑运算符
符号 | 说明 | 示例 |
AND, && | 逻辑与 | name="通知" AND label="通知" |
OR, || | 逻辑或 | |
NOT, ! | 逻辑非 |
字符串比较
关键字 | 说明 | 示例 |
BEGINSWITH | 以某个字符串开始 | name BEGINSWITH "屏幕" |
ENDSWITH | 以某个字符串结束 | name ENDSWITH "时间" |
CONTAINS | 包含 | name CONTAINS "使用时间" |
LIKE | 通配符 | name LIKE '*Total: $*' |
MATCHES | 正则匹配 | value MATCHES '.*of 7' |
默认情况下,字符串比较是大小写和变音敏感的,可以在关键字后面加上
[cd]
,[c]
不区分大小写,[d]
表示不区分变音符号变音符号是附加在字母上的符号,用于提示发音或区分相似的单词。很多语言使用变音符,比如法语、西班牙语等。比如法语单词:Pêche 桃子
classChain定位
classChain是Predicate和Xpath定位的结合,搜索效率比XPath更高
class chain 定位方法由mykola-mokhnach开发,和XPath比较类似,可以实现分层查询,但它的查询性能更高,通过将class chain查询映射到一系列的XCUITest调用中,仅查找子节点,不像XPath那样递归地查询整个UI树
儿子节点搜索
选择儿子元素,类似于XPath语法中的反斜杠/,示例如下:
XCUIElementTypeWindow[`label BEGINSWITH "text"`][-1] # 选择label以foo开头的最后一个
XCUIElementTypeWindow/XCUIElementTypeButton[3] # 选择window的儿子元素XCUIElementTypeButton的第3个(索引从1开始)
XCUIElementTypeWindow/*[3] # 选择window的第3个儿子元素
XCUIElementTypeWindow # 选择所有子窗口
XCUIElementTypeWindow[2] # 选择第二个窗口
XCUIElementTypeWindow[2]/XCUIElementTypeAny # 选择第二个子窗口的所有子元素
子孙节点搜索
类似于XPath语法中的双反斜杠//,示例如下:
**/XCUIElementTypeCell[`name BEGINSWITH "A"`][-1]/XCUIElementTypeButton[10] # 选择name以A开头的最后一个Cell元素的第10个子元素
**/XCUIElementTypeCell[`name BEGINSWITH "B"`] # 选择name以B开头的所有Cell元素
**/XCUIElementTypeCell[`name BEGINSWITH "C"`]/XCUIElementTypeButton[10] # 选择name以C开头的第一个Cell元素的第10个子元素
**/XCUIElementTypeCell[`name BEGINSWITH "D"`]/**/XCUIElementTypeButton # 选择name以D开头的第一个Cell元素下所有后代Button
五、元素
单个元素实例
client(定位元素).find_elements()
多个元素实例
client(nameCntains='element_name').find_elements()
还可以通过索引返回单个元素实例
client(nameCntains='element_name', index=2).find_elements() # 返回匹配的第三个元素
# 或者
client(nameCntains='element_name')[2].find_elements()
判断元素是否存在
client(定位元素).exists # 返回True或者False
获取元素属性
element = client(元素定位)
# 获取元素属性有以下几类,返回结果以实际为准
element.className # XCUIElementTypeButton属性
element.name # XCUIElementTypeButton属性
element.visible # True
element.value # 空
element.label # label元素名称
element.text # 当前定位元素方式内容
element.enabled # True
element.displayed # True
element.accessible # True
x, y, w, h = element.bounds # Rect(x=, y=, width=, height=)
六、元素操作
点击元素click\tap
# 元素点击
client(定位方式).tap()
client(定位方式).tap_hold(2.0) # 长按2秒
client(定位方式).click()
client(定位方式).click_exists() # 返回True或者False
client(定位方式).click_exists(timeout=5.0) # 超时等待5秒
#坐标点击
client.tap(x, y) # 通过坐标进行点击
client.click(x, y) # 通过坐标进行点击
client.double_tap(x, y) # 双击
输入set_text/清除clear_text
element= client(元素定位)
# 输入
element.set_text('contenct')
# 清除
element.clear_text()
# 删除多个字符
element.set_text("\b\b\b\n") # 删除3个字符
# 输入文本+确认
element.set_text('contenct\n') # \n为确认,类似于输入完按Enter换行
滑动swipe
WDA的滑动方式有两种 ,一个是根据坐标滑动,一个是安装方向滑动
client.swipe(fx, fy, tx, ty, duration=0.5) # 从(fx, fy)滑到(tx, ty),坐标值可以是迅速值或者百分比,duration单位秒
client.swipe_left() # 向左滑动
client.swipe_right() # 向右滑动
client.swipe_up() # 向上滑动
client.swipe_down() # 向下滑动