Airtest之iOS API汇总

上期回顾:Airtest之安卓API汇总


以下基于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
tpos – end point
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 使用浅析

---------------------------------------------------------------------------------

关注微信公众号即可在手机上查阅,并可接收更多测试分享~

原文地址:https://www.cnblogs.com/songzhenhua/p/15315241.html