Airtest之iOS API汇总
上期回顾:Airtest之安卓API汇总
2024.2.25更新:新增剪切板、Airtest1.3.3touch/swipe支持绝对坐标和相对坐标
2023.9.3更新:Airtest1.3.0.1新增iOS设备相关接口
以下基于airtest1.2.0
Airtest核心API文件路径:
your_python_path/site-packages/airtest/core/api.py
iOS API文件路径:
your_python_path/site-packages/airtest/core/ios/ios.py
ios.py中定义了一个IOS类,继承自Device基类,Device类里只有方法名定义,具体实现是在各个设备类里,比如苹果设备的实现就是在ios.py中的IOS类里。
初始化方法里定义了很多类变量,比如cap方式、_is_pad、info(设备信息)等,这个自己看代码吧,下面我们重点介绍一下iOS类里都有哪些方法。
def __init__(self, addr=DEFAULT_ADDR):
super(IOS, self).__init__()
# if none or empty, use default addr
self.addr = addr or DEFAULT_ADDR
# fit wda format, make url start with http://
# eg. http://localhost:8100/ or http+usbmux://00008020-001270842E88002E
if not self.addr.startswith("http"):
self.addr = "http://" + addr
"""here now use these supported cap touch and ime method"""
self.cap_method = CAP_METHOD.WDACAP
self.touch_method = TOUCH_METHOD.WDATOUCH
self.ime_method = IME_METHOD.WDAIME
# wda driver, use to home, start app
# init wda session, updata when start app
# use to click/swipe/close app/get wda size
wda.DEBUG = False
self.driver = wda.Client(self.addr)
# record device's width
self._size = {'width': None, 'height': None}
self._current_orientation = None
self._touch_factor = None
self._last_orientation = None
self._is_pad = None
self._device_info = {}
info = self.device_info
self.instruct_helper = InstructHelper(info['uuid'])
# start up RotationWatcher with default session
self.rotation_watcher = RotationWatcher(self)
self._register_rotation_watcher()
self.alert_watch_and_click = self.driver.alert.watch_and_click
在细讲之前,让我们再来回顾一下iOS设备的链接方法:airtest之使用tidevice工具轻松连接iOS
ios.py中方法详细说明
1.decorator_retry_session
装饰器,当因为session失效而操作失败时,尝试重新获取session,最多重试3次
2.decorator_retry_for_class
装饰器,为IOS类里的所有method添加装饰器 decorator_retry_session
以下为IOS类中内容
3.uuid
类属性
返回:
连接字符串,如http+usbmux://231ad3452c53a702
实际案例
auto_setup(__file__, devices=["ios:///http+usbmux://231ad21953be1eaf92"])
print(G.DEVICE.uuid)
4.is_pad
类属性,判断是否是ipad(或 6P/7P/8P),如果是,在横屏+桌面的情况下,坐标需要切换成竖屏坐标才能正确点击(WDA的bug)
返回:
是返回True,否返回False
5.device_info
类属性,获取设备信息
返回:
dict for device info,
eg. AttrDict({
'timeZone': 'GMT+0800',
'currentLocale': 'zh_CN',
'model': 'iPhone',
'uuid': '90CD6AB7-11C7-4E52-B2D3-61FA31D791EC',
'userInterfaceIdiom': 0,
'userInterfaceStyle': 'light',
'name': 'iPhone',
'isSimulator': False})
6.window_size()
返回窗口大小
返回:
namedtuple:
Size(wide , hight)
实际案例
auto_setup(__file__, devices=["ios:///http+usbmux://231ad21953be1eaf92"])
print(G.DEVICE.window_size())
7.orientation
return device oritantation status in LANDSACPE POR
类属性,返回设备方向状态
返回:
竖屏返回PORTRAIT,横屏返回LANDSCAPE
8.get_orientation()
self.driver.orientation只能拿到LANDSCAPE,不能拿到左转/右转的确切方向
因此手动调用/rotation获取屏幕实际方向
返回:
摄像头朝左的横屏返回LANDSCAPE
摄像头朝右的横屏返回UIA_DEVICE_ORIENTATION_LANDSCAPERIGHT
9.display_info
类属性,显示信息
返回:
{'width': 640, 'height': 1136, 'orientation': 'PORTRAIT', 'physical_width': 640,
'physical_height': 1136, 'window_width': 320, 'window_height': 568}
10.touch_factor
类属性
use session.scale can get UIKit scale factor
so self._touch_factor = 1 / self.driver.scale, but the result is incorrect on some devices(6P/7P/8P)
返回:
self._touch_factor = float(self._size['window_height']) / float(height)
11.get_render_resolution()
返回旋转后的渲染分辨率
返回:
(offset_x, offset_y, offset_width and offset_height of the display)
如(0, 0, 640, 1136)
12.get_current_resolution()
返回当前分辨率
返回:
如:
竖屏(640, 1136)
横屏(1136, 640)
13.home()
点击home键
14.snapshot(filename=None, strType=False, quality=10, max_size=None)
截屏
参数:
filename: 截图文件名称
quality: 截图质量,范围为[1, 99]
max_size: 限制图片最大尺寸,如1200
返回:
display the screenshot
示例:
详见Airtest核心API汇总
15.touch(pos, duration=0.01)
点按
参数:
pos: 坐标(x, y), 可以是相对坐标或绝对坐标
duration(optional): 点按持续时间
返回:
None
示例:
touch((100, 100)) # 点击绝对坐标(100,100)
touch((0.5, 0.5), duration=1) # 点相对坐标(0.5,0.5),屏幕中心
16.double_click(pos)
双击
参数:
pos: 坐标(x, y)
17.swipe(fpos, tpos, duration=0, *args, **kwargs)
滑动
参数:
fpos – start point,绝对/相对坐标(x, y)
tpos – end point,绝对/相对坐标(x, y)
duration (float) – start coordinate press duration (seconds), default is 0
返回:
None
示例:
swipe((1050, 1900), (150, 1900)) # 绝对坐标滑动
swipe((0.2, 0.5), (0.8, 0.5)) # 相对坐标滑动
18.keyevent(keyname, **kwargs)
在设备上执行keyevent,只支持home/volumeUp/volumeDown
参数:
keyname – home/volumeUp/volumeDown
示例:
keyevent("volumeUp") # 音量增加
keyevent("volumeDown") # 音量减少
19.text(text, enter=True)
输入文本
参数:
text: 要输入的文本
enter: 是否按回车
返回:
None
示例:
text("qasite") # 输入“qaiste”并回车
text("测试工程师小站", enter=False) # 输入"测试工程师小站"
20.start_app(package, *args)
启动应用
参数:
package: the app bundle id, e.g com.apple.mobilesafari
返回:
None
示例:
start_app('com.apple.mobilesafari')
21.stop_app(package)
停止应用
参数:
package: the app bundle id, e.g com.apple.mobilesafari
返回:
None
示例:
stop_app('com.apple.mobilesafari')
22.app_state(package)
获取应用当前状态
参数:
package: the app bundle id, e.g com.apple.mobilesafari
返回:
1(not running) 2(running in background) 3(running in foreground) 4(running)
我个人理解running in foreground是指有UI界面的app被HOME后,但仍在运行;running in background应该是本身就是以服务的形式在后台启动运行的0
如:
{
"value": 4,
"sessionId": "0363BDC5-4335-47ED-A54E-F76A65"
}
示例:
dev = device()
start_app("com.apple.mobilesafari")
sleep(2.0)
print("此时的包体状态为:"+str(dev.app_state("com.apple.mobilesafari")["value"]))
home()
sleep(2.0)
print("此时的包体状态为:"+str(dev.app_state("com.apple.mobilesafari")["value"]))
stop_app("com.apple.mobilesafari")
sleep(2.0)
print("此时的包体状态为:"+str(dev.app_state("com.apple.mobilesafari")["value"]))
输出:
此时的包体状态为:4
此时的包体状态为:3
此时的包体状态为:1
23.app_current()
返回当前运行的应用。可能在某些型号设备无效
返回:AttrDict
示例:
dev = device()
start_app("com.apple.mobilesafari")
print(dev.app_current())
keyevent("HOME")
sleep(1.0)
print(dev.app_current())
输出:
AttrDict({'processArguments': {'env': {}, 'args': []}, 'name': 'AX error -25205', 'pid': 226, 'bundleId': 'com.apple.mobilesafari'})
AttrDict({'processArguments': {'env': {}, 'args': []}, 'name': 'AX error -25205', 'pid': 58, 'bundleId': 'com.apple.springboard'})
24.is_locked()
判断设备是否锁屏。可能在某些型号设备无效
返回:
锁屏返回True,没锁返回False
25.unlock()
解锁设备、解锁屏幕、按2下HOME键。可能在某些型号设备无效
返回:
None
26.lock()
设备锁屏。可能在某些型号设备无效
返回:
None
27.alert_accept()
点击有2个按钮的弹窗的右边的按钮。可能在某些型号设备无效
对于拥有2个按钮的iOS弹窗来说,一般情况下,确认按钮都在右边,所以alert_accept会点击右边的按钮。这只是一个方便使用的接口,不一定适用于所有的情况,如果遇到点击情况不符合预期,可以用按指定按钮名字来点击的接口alert_click()
返回:
None
28.alert_dismiss()
点击有2个按钮的弹窗的左边的按钮。可能在某些型号设备无效
对于拥有2个按钮的iOS弹窗来说,一般情况下,取消按钮都在左边,所以alert_dismiss会点击左边的按钮。这只是一个方便使用的接口,不一定适用于所有的情况,如果遇到点击情况不符合预期,可以用按指定按钮名字来点击的接口alert_click()
返回:
None
29.alert_wait(time_counter=2)
判断X秒内弹窗是否出现。可能在某些型号设备无效
参数:
time_counter – 等待时间,默认2秒
返回:
X秒内弹窗出现返回True,否则False
30.alert_buttons()
获取弹窗按钮文本。可能在某些型号设备无效
返回:
如,("设置", "好")
31.alert_exists()
判断弹窗是否存在。可能在某些型号设备无效
返回:
True or False
32.alert_click(buttons)
点击弹窗的指定按钮。可能在某些型号设备无效
参数:
buttons:按钮列表,如['设置', '允许', '好']
返回:
按顺序查找弹窗是否有列表中的按钮,有则点击第一个匹配的;没有则报错
示例:
dev = device()
dev.alert_click(['允许','好'])
33.home_interface()
判断是否在HOME页。可能在某些型号设备无效
返回:
True or False
34.alert.text
返回弹窗上的提示文字(非按钮)
示例:
dev = device()
print(dev.driver.alert.text)
35.alert_watch_and_click(buttons: Optional[list] = None,interval: float = 2.0)
监控弹窗出现并且点击指定按钮
参数
buttons: 要点击的按钮列表,如["确定", "允许", "以后"]
interval: 检查间隔,默认2秒
示例:
from airtest.core.ios.ios import IOS, wda
ios = IOS("http://localhost:8100/")
# 默认情况下监控此类弹窗:["使用App时允许", "好", "稍后", "稍后提醒", "确定", "允许", "以后"]
with ios.alert_watch_and_click():
sleep(5)
# 监控指定弹窗出现并点击
with ios.alert_watch_and_click(["Cancel"]):
sleep(5)
# 设置监控的时间间隔为2.0s
with ios.alert_watch_and_click(interval=2.0):
sleep(5)
这里为什么要用with呢,因为其实现啊,看看源码:
# your_python_path/site-packages/wda/__init__.py
@contextlib.contextmanager
def watch_and_click(self,
buttons: Optional[list] = None,
interval: float = 2.0):
""" watch and click button
Args:
buttons: buttons name which need to click
interval: check interval
"""
if not buttons:
buttons = self.DEFAULT_ACCEPT_BUTTONS
event = threading.Event()
def _inner():
while not event.is_set():
try:
alert_buttons = self.buttons()
logger.info("Alert detected, buttons: %s", alert_buttons)
for btn_name in buttons:
if btn_name in alert_buttons:
logger.info("Alert click: %s", btn_name)
self.click(btn_name)
break
else:
logger.warning("Alert not handled")
except WDARequestError:
pass
time.sleep(interval)
threading.Thread(name="alert", target=_inner, daemon=True).start()
yield None
event.set()
新手的你可能还有疑问,那就了解一下with和yield吧,另外也去了解一下threading。
python的with用法、Python yield 使用浅析
36.install_app(file_or_url)
从本地或网络安装IPA包
参数
file_or_url - 本地路径或网址
示例
# 支持ipa包安装
install_app(r"D:\demo\qasite.ipa")
# 也支持通过下载链接安装APP
install_app("http://www.example.com/test.ipa")
37.uninstall_app(bundle_id)
卸载安装包
示例
# 参数为Bundle ID即iOS包的包名
uninstall_app("com.qasite.wechat")
38.list_app(type="user")
返回应用列表
参数:
type – 可输入参数"user", "system", "all"
返回:
应用列表
示例:
dev = device()
user_app = dev.list_app("user")
print(f"user_app:\n{user_app}")
39.set_clipboard(content, wda_bundle_id=None)
设置剪贴板内容
参数:
content - 要设置的内容wda_bundle_id – 当手机上装有多个WDA时才用,一般用不上
示例:
set_clipboard("qasite")
40.get_clipboard(wda_bundle_id=None)
获取剪贴板内容
参数:
wda_bundle_id – 当手机上装有多个WDA时才用,一般用不上
示例:
text = get_clipboard()
print(f'剪贴板内容:{text}')
41.get_ip_address()
获取手机IP
42.device_status()
获取设备状态
示例:
dev = device()
print(dev.device_status())
43.tidevice相关接口
Airtest1.3.0.1新增封装了tidevice功能接口
-
devices :列出USB连接的所有设备的 UDID 列表
from airtest.core.ios.ios import TIDevice
udid = TIDevice.devices()
print(f"TIDevice.devices():\n{udid}")
输出
['f83a2d08deb8c22ce6338e35328f5cfcaaf5d3f4']
如果是要查看当前连接的iOS设备,直接这样获取即可:print(dev.udid)
-
list_app :列出手机上安装的应用列表,支持对类型进行筛选,包括 user/system/all
Airtest接口中已有一样的API,用那个就行了 -
list_wda :列出手机上安装的所有WDA的 bundleID
一般我们也不会同时装好多个wda吧,基本不用 -
device_info :获取手机信息
print(f"TIDevice.device_info():\n{TIDevice.device_info(udid)}")
# 输出
{'productVersion': '15.7.3', 'productType': 'iPhone9,2', 'modelNumber': 'MNRL2', 'serialNumber': 'F2LSGNHG52', 'timeZone': 'Asia/Shanghai', 'uniqueDeviceID': 'f83a2d08deb8caaf5d3f4', 'marketName': 'iPhone 7 Plus'}
-
install_app :安装ipa包,支持本地路径或URL
Airtest接口中已有一样的API,用那个就行了 -
uninstall_app:卸载 bundle_id 对应的包体
Airtest接口中已有一样的API,用那个就行了 -
start_app :启动 bundle_id 对应的包体
Airtest接口中已有一样的API,用那个就行了 -
stop_app :停止 bundle_id 对应的包体
Airtest接口中已有一样的API,用那个就行了 -
ps :获取当前的进程列表
想获取app包名时,也可以用此方法
print(f"TIDevice.ps():\n{TIDevice.ps(udid)}")
# 输出
[{'pid': 4848, 'name': 'StoreKitUIService', 'bundle_id': 'com.apple.ios.StoreKitUIService', 'display_name': 'iTunes'}, {'pid': 217, 'name': 'Spotlight', 'bundle_id': 'com.apple.Spotlight', 'display_name': '搜索'}, {'pid': 5176, 'name': 'TestFlight', 'bundle_id': 'com.apple.TestFlight', 'display_name': 'TestFlight'}, {'pid': 7699, 'name': 'AppStore', 'bundle_id': 'com.apple.AppStore', 'display_name': 'App Store'}, {'pid': 5050, 'name': 'SafariViewService', 'bundle_id': 'com.apple.SafariViewService', 'display_name': 'Safari浏览器'}, {'pid': 6800, 'name': 'Preferences', 'bundle_id': 'com.apple.Preferences', 'display_name': '设置'}]
-
ps_wda :获取当前启动中的WDA列表
暂时没想到使用场景 -
xctest:启动WDA
import threading
import time
from airtest.core.ios.ios import TIDevice
wda_bundle_id = TIDevice.list_wda(self.udid)[0]
# 创建一个线程,执行xctest
t = threading.Thread(target=TIDevice.xctest, args=(self.udid, wda_bundle_id), daemon=True)
t.start()
time.sleep(5)
ps_wda = TIDevice.ps_wda(self.udid)
print(ps_wda)
self.assertIn(wda_bundle_id, ps_wda)
time.sleep(5)
# 终止线程
t.join(timeout=3)
---------------------------------------------------------------------------------
关注微信公众号即可在手机上查阅,并可接收更多测试分享~