赞
踩
目录
使用tidevice工具启动WDA代理服务见文章:基于tidevice实现iOS app自动化使用详解-CSDN博客
1、安装:
pip install weditor==0.6.4
2、启动weditor
python -m weditor
3、启动WebDriverAgent服务
tidevice wdaproxy -B WebDriverAgent_bundle_id --port 8100
# 指定设备启动WebDriverAgent
tidevice -u &UDID wdaproxy -B WebDriverAgent_bundle_id --port 8100
Facebook-wda库(wda是其简称)是一个基于WebDriverAgent的Python客户端库,用于实现iOS设备的自动化测试。下面是Facebook-wda库实现iOS自动化的底层原理:
综上所述,Facebook-wda库通过WebDriver协议与iOS设备的WebDriverAgent进行通信,以实现iOS设备的自动化测试。它利用了WebDriverAgent作为中间件,将客户端的请求转化为XCTest和XCUITest代码,从而控制设备执行操作。通过这种方式,Facebook-wda库实现了iOS自动化测试的底层原理。
pip3 install -U facebook-wda
- import wda
-
- wda.DEBUG = False # default False
- wda.HTTP_TIMEOUT = 180.0 # default 180 seconds
- wda.DEVICE_WAIT_TIMEOUT = 180.0 # default 180 seconds
1、默认一个设备的情况
- import wda
-
- # 等价于wda.Client()方式,默认服务地址为http://localhost:8100,且连接的设备只有一个的情况
- c = wda.Client('http://localhost:8100')
- # 检查连接状态,state为success表示连接成功
- print(c.status())
2、使用USBClient创建客户端
- # 只有一个设备的情况,可以不添加参数
- c = wda.USBClient()
-
- # 指定设备的udid、WDA的端口号和wda_bundle_id连接
- c = wda.USBClient("$UDID", port=8100, wda_bundle_id="$wda_bundle_id")
-
- # 通过DEVICE_URL访问
- c = wda.Client("http+usbmux://{udid}:8100".format(udid="$UDID"))
-
- print(c.status())
- # 注意每次启动都是冷启动,也就是如果app打开,会先kill掉,再启动
- c.session("com.apple.Preferences")
-
- # 如果已启动且在后台会被拉起到前台
- # 如果已启动且在前台,不做任何操作,结束
- # 如果未启动,则冷启动该应用
- c.session().app_activate("com.apple.Preferences")
-
- # 作用和app_activate()一样,但是支持更多的参数
- c.session().app_start("com.apple.Preferences")
- # 关闭APP
- c.session().app_terminate("com.apple.Preferences")
- # 获取APP状态信息
- print(c.session().app_state("com.apple.Preferences"))
-
- # 输出结果
- {'value': 4, 'sessionId': 'B5976-F91E-444A-876E-1DA0', 'status': 0}
- value 表示的意思 1: 未启动, 2: 驻留在后台, 4: 正在运行
- # 获取当前APP的运行信息
- print(c.app_current())
- # 回到手机主页面
- c.home()
- c.press("home")
- # 调节音量
- c.press("volumeUp")
- c.press_duration("volumeUp", 3) # 长按3s音量上键
- c.press("volumeDown")
- c.press_duration("volumeDown", 3) # 长按3s音量下键
- c.locked() # 返回锁定状态True/False
- c.lock() # 锁屏
- c.unlock() # 解锁
- # 截图,save(fp) 传文件路径地址
- c.screenshot().save("./tmp.png")
-
- from PIL import Image
- # 旋转90度截屏(逆时针旋转)
- c.screenshot().transpose(Image.ROTATE_90).save("./tmp2.png")
- # 查看设备状态信息
- print(c.status())
-
- # 获取设备信息
- print(c.device_info())
-
- # 获取电量信息
- print(c.battery_info())
-
- # 获取分辨率
- print(c.window_size())
- # 设置强制等待的时间为3秒,等价于:time.sleep(3)
- c.sleep(3)
-
- # 设置隐式等待的时间为10
- c.implicitly_wait(10)
通过id进行定位,这里的id是指页面元素字段identifier
c(id='element_id')
c(className="element_className")
- c(value="element_value")
- # 模糊查找
- c(valueContains="part element_value")
- c(label="element_label")
- # 模糊查找
- c(labelContains="part element_label")
- c(name="element_name")
- # 模糊查找
- c(nameContains="part element_name")
- # 正则表达式
- c(nameMatches="regex string")
c(className="element_className", name="element_name")
- # 判断子元素是否存在
- c(className='element_className').c(name='child_element_name').exists
xpath使用文档手册:xpath使用手册 - 简书
说明:单属性定位的前提是所选择定位的属性的值是唯一的才能准确定位;当然即使属性值不唯一,也可以进行定位,返回值默认是定位到的第一个元素。
书写格式: //*[@resource-id=’id属性’] 或 //*[contains(@resource-id, ‘部分id属性’)]
示例:
- # 示例1:完全匹配
- c(xpath="//*[@name='element_name']") 或 c.xpath("//*[@name='element_name']")
-
- # 示例2:模糊匹配
- c(xpath="//*[contains(@name, 'element_name')]")
注:可以通过属性 id、value、label、name进行定位,注意:className属性识别不到,避免使用。
说明:有时仅靠单一属性是很难准确定位的,这个时候就需要使用待定位元素的多个属性对该元素进行定位,通过 id、className、value、label、name这些属性任意组合实现准确定位
示例:
print(c.xpath("//*[@name='element_name' and contains(@value, 'part element_value')]").exists)
print(c.xpath("//XCUIElementTypeCell[2]//*[@label='收入']").exists)
print(c.xpath("//*[@name='element_name' and @id='rawIdentifier_value')]").exists)
说明:对于某些元素来说本身属性较少,无法通过组合定位的方式实现准确定位,那么就需要通过层级定位的方式实现准确定位,也就是通过其周围的元素来定位待定位元素
print(c.xpath("//XCUIElementTypeCell[2]//*[@label='收入']").exists)
print(c.xpath("//*[@label='收入']/..").exists)
print(c.xpath("//*[@label='收入']/..//*[contains(@label, '银行')]").exists)
print(c.xpath("//*[@label='收入']/../..//XCUIElementTypeCell[3]//*[@name='晚餐']").exists)
使用 Predicate 进行元素定位的优势:
注意:默认情况下,字符串比较是大小写和变音敏感的,可以在关键字后面加上[cd],[c]不区分大小写,[d]表示不区分变音符号
基本比较
符号 | 说明 | 示例 |
=, == | 等于 | name == “hello” |
> | 大于 | label > 10 |
小于 | label < 10 | |
>=, => | 大于等于 | label >= 10 |
小于等于 | label | |
!=, <> | 不等于 | label != “hello” |
集合操作
符号 | 说明 | 示例 |
ANY, SOME | 满足表达式的任意元素 | ANY value < 18 |
ALL | 满足表达式的所有元素 | ALL value < 18 |
NONE | 不包含满足表达式的任意元素 | NONE value < 18 |
IN | 元素在集合中 | name IN { ‘hello’, ‘world’ } |
BETWEEN | 位于某个范围 | value BETWEEN { 0 , 33 } |
array[index] | 数组array中指定索引的元素 | |
array[FIRST] | 数组中的第一个元素 | |
array[LAST] | 数组中的最后一个元素 | |
array[SIZE] | 指定数组大小 |
布尔值
符号 | 说明 | 示例 |
TRUEPREDICATE | TRUE | |
FALSEPREDICATE | FALSE |
逻辑运算符
符号 | 说明 | 示例 |
AND, && | 逻辑与 | name="hello" AND label="hello" |
OR, || | 逻辑或 | |
NOT, ! | 逻辑非 |
字符串比较
关键字 | 说明 | 示例 |
BEGINSWITH | 以某个字符串开始 | name BEGINSWITH "hel" |
ENDSWITH | 以某个字符串结束 | name ENDSWITH "lo" |
CONTAINS | 包含 | name CONTAINS "llo" |
LIKE | 通配符 | name LIKE '*llo' |
MATCHES | 正则匹配 | value MATCHES '.*llo' |
使用示例:
print(c(predicate='name BEGINSWITH "工资" and label="工资到账"').exists)
print(c(predicate="name CONTAINS[c] 'llo'").exists)
- # 返回所有符合条件的元素
- print(c(predicate="name CONTAINS[c] 'llo' AND TRUEPREDICATE").find_elements())
classChain 定位策略在某些情况具有以下优势:
可以理解classChain是Predicate和Xpath定位的结合,搜索效率比XPath更高,可以直接查找子节点,不需要对整个页面进行遍历。
注意事项:在使用classChain定位是,一定要正确的知道页面的层级关系,可以通过c.source()函数查看页面结构。
示例:
- print(c(classChain='XCUIElementTypeWindow').find_elements()) # 获取所有子窗口
- print(c(classChain='XCUIElementTypeWindow[1]')) # 获取第二个窗口(索引下标从1开始)
- print(c(classChain='XCUIElementTypeWindow[1]/*').find_elements()) # 获取第一个窗口下的所有子元素
- print(c(classChain='XCUIElementTypeWindow[1]/*[1]').find_elements()) # 获取第一个窗口下的第一个子元素
- print(c(classChain='XCUIElementTypeWindow[1]/XCUIElementTypeButton[1]').find_elements()) # 获取第一个窗口下的第一个XCUIElementTypeButton元素
- print(c(classChain='**/XCUIElementTypeCell/XCUIElementTypeStaticText[`label="工资收入"`]').exists) # 匹配所有Cell元素下满足label="工资收入"的Text元素
- print(c(classChain='**/XCUIElementTypeCell[1]/XCUIElementTypeStaticText').find_elements()) # 获取第一个Cell元素下所有的Text元素
- print(c(className="XCUIElementTypeStaticText", index=1).find_elements())
- 或
- print(c(className="XCUIElementTypeStaticText")[1].find_elements())
- e = c("元素定位")
-
- print(e.className)
- print(e.name)
- print(e.visible)
- print(e.value)
- print(e.label)
- print(e.text)
- print(e.enabled)
- print(e.displayed)
- print(e.accessible)
- print(e.bounds) # Rect(x=, y=, width=, height=)
- c(className="XCUIElementTypeButton", name="1").click() # 点击元素
- c(className="XCUIElementTypeButton", name="1").click_exists() # 判断是否可点击
- c(className="XCUIElementTypeButton", name="1").click(timeout=1) # 设置超时等待时间单位秒
-
- c.click(x, y, duration) # 通过坐标点击 duration表示点击时长
- c(className="XCUIElementTypeButton", name="1").tap() # 和click一样
- c(className="XCUIElementTypeButton", name="delet noire").tap_hold(3) # 长按3秒
-
- c.tap(x, y) # 通过坐标点击
- c.tap_hold(x, y, duration) # 通过坐标点击 duration表示点击时长
- c.double_tap(x, y) # 双击
- c.xpath("//*[@label='搜索']").set_text('123') # 输入值
- c.xpath("//*[@label='搜索']").clear_text() # 清除值
- c.xpath("//*[@label='搜索']").set_text("\b\b\b") # 删除3个字符,结尾也可以加\n
- c.xpath("//*[@label='搜索']").set_text('123\n') # 输入内容并确认,\n表示确认。类似输入完按Enter换行
- c.swipe(x1, y1, x2, y2, duration=1) # 从(x1, y1)滑到(x2, y2),duration 持续时间 单位秒
- c.swipe_left() # 向左滑动
- c.swipe_right() # 向右滑动
- c.swipe_up() # 向上滑动
- c.swipe_down() # 向下滑动
- # 放大 scale:缩放比例 speed:缩放速度 要求:scale>1 speed>0
- c(className="Map").pinch(2, 1) # scale=2, speed=1
- # 缩小,这里效果不好 要求:scale<1 speed<0
- c(className="Map").pinch(0.1, -1) # scale=0.1, speed=-1
- # irection 为 "up"、"down"、"left"、"right" 时,表示在指定方向上滚动。
- # distance:滚动的距离,仅在 direction 不为 "visible" 时起作用。默认值为 1.0,表示元素的宽度或高度的倍数。
- c(name="hello").scroll('down', 10) # 向下方向滚动hello元素宽度的 10 倍的距离(如果是向左/右 滚动元素的高度距离倍数)
- print(s.alert.exists)
- print(s.alert.text)
- s.alert.accept() # Actually do click first alert button
- s.alert.dismiss() # Actually do click second alert button
- s.alert.wait(5) # if alert apper in 5 second it will return True,else return False (default 20.0)
- s.alert.wait() # wait alert apper in 2 second
-
- s.alert.buttons()
- # example return: ["设置", "好"]
-
- s.alert.click("设置")
- s.alert.click(["设置", "信任", "安装"]) # when Arg type is list, click the first match, raise ValueError if no match
- with c.alert.watch_and_click(['好', '确定']):
- s(label="Settings").click() #
-
- # 默认按钮值:["使用App时允许", "好", "稍后", "稍后提醒", "确定", "允许", "以后"]
- with c.alert.watch_and_click(interval=2.0): # default check every 2.0s
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。