常见问题与代码示例
如何进行脚本初始化
air脚本:auto_setup()
自动配置运行环境的接口,可以配置当前脚本所在路径、使用的设备、log内容的保存路径、项目根目录和截图压缩精度:
auto_setup(basedir=None, devices=None, logdir=None, project_root=None, compress=None)
接口示例:
auto_setup(__file__)
auto_setup(__file__, devices=["android://127.0.0.1:5037/emulator-5554?cap_method=JAVACAP&&ori_method=MINICAPORI&&touch_method=MINITOUCH"], logdir=True, project_root=r"D\test", compress=90)
如何连接/使用设备
请注意: 更多设备相关的信息,请参考文档
连接设备:connect_device(URI)
连接设备的接口,需要传入用于初始化设备的URI字符串,示例:
# 连接安卓设备
connect_device("Android://127.0.0.1:5037/SJE5T17B17")
# 连接iOS设备
connect_device("iOS:///127.0.0.1:8100")
# 连接Windows窗口
connect_device("Windows:///123456")
# 连接模拟器
connect_device("Android://127.0.0.1:5037/127.0.0.1:62001?cap_method=JAVACAP&&ori_method=ADBORI")
连接设备:init_device()
初始化设备的接口,需要传入设备平台、设备的uuid和可选参数等,其中uuid为,Android的序列号,Windows的窗口句柄,或iOS的uuid:
init_device(platform='Android', uuid=None, **kwargs)
接口使用示例:
# 连接安卓设备
init_device(platform="Android",uuid="SJE5T17B17",cap_method="JAVACAP")
# 连接Windows窗口
init_device(platform="Windows",uuid="123456")
获取当前设备:device()
返回当前正在使用中的设备实例,用法示例如下:
dev = device()
dev.swipe_along([[959, 418],[1157, 564],[1044, 824],[751, 638],[945, 415]])
设置当前设备:set_current()
设置当前的使用设备,可以用于在多设备之间切换使用,示例如下:
# 第一种:传入数字0、1、2等,切换当前操作的手机到Airtest连接的第1台、第2台手机
set_current(0)
set_current(1)
# 第二种:切换当前操作的手机到序列号为serialno1、serialno2的手机
set_current("serialno1")
set_current("serialno2")
如何进行坐标点击/坐标滑动
坐标点击
在设备上进行点击操作,可以传入点击位置、点击次数等参数,不同平台下的可选参数稍有不同,示例如下:
# 传入绝对坐标作为点击位置
touch([100,100])
# 传入Template图片实例,点击截图中心位置
touch(Template(r"tpl1606730579419.png", target_pos=5, record_pos=(-0.119, -0.042), resolution=(1080, 1920)))
# 点击2次
touch([100,100],times=2)
# Android和Windows平台下,可以设置点击时长
touch([100,100],duration=2)
坐标滑动
在设备上进行滑动操作,有2种传参方式,一种是传入滑动的起点和终点,一种是传入滑动的起点和滑动方向vector,示例如下:
# 传入绝对坐标作为滑动的起点和终点
swipe([378, 1460],[408, 892])
# 传入图像作为起点,沿着某个方向滑动
swipe(Template(r"tpl1606814865574.png", record_pos=(-0.322, 0.412), resolution=(1080, 1920)), vector=[-0.0316, -0.3311])
# 常见的还可以设置滑动的持续时长
swipe([378, 1460],[408, 892],duration=1)
如何安装/启动/卸载应用
启动应用:start_app()
在设备上启动目标应用,需传入应用的包名,支持Android和iOS平台,示例:
start_app("com.netease.cloudmusic")
终止应用运行:stop_app()
在设备上终止目标应用的运行,需传入应用的包名,支持Android和iOS平台,示例:
stop_app("com.netease.cloudmusic")
清除应用数据:clear_app()
清理设备上的目标应用数据,需传入应用的包名,仅支持Android平台 ,示例:
clear_app("com.netease.cloudmusic")
安装应用:install()
安装应用到设备上,需传入完整的apk的安装路径,仅支持Android平台,示例:
install(r"D:\demo\tutorial-blackjack-release-signed.apk")
卸载应用:uninstall()
卸载设备上的应用,需传入被卸载应用的包名,仅支持Android平台,示例:
uninstall("com.netease.cloudmusic")
按键事件:keyevent
Android的keyevent
等同于执行 adb shell input keyevent KEYNAME
,示例如下:
keyevent("HOME")
# The constant corresponding to the home key is 3
keyevent("3") # same as keyevent("HOME")
keyevent("BACK")
keyevent("KEYCODE_DEL")
Windows的keyevent
与安卓不同,Windows平台使用 pywinauto.keyboard module
模块来进行按键输入,示例:
keyevent("{DEL}")
# 使用Alt+F4关闭Windows窗口
keyevent("%{F4}")
iOS的keyevent
目前仅支持HOME键:
keyevent("HOME")
如何输入文本
在设备上输入文本,文本框需要处于激活状态(即先点击文本框,再使用 text()
接口进行输入)。示例如下:
touch(文本框的Template实例)
text("输入的文本")
# 默认情况下,text是带回车的,不需要可以传入False
text("123",enter=False)
# 安卓平台下,还支持输入后点击软键盘的搜索按钮
text("123",enter=False,search=True)
如何删除文本
通过 keyevent
接口删除单个字符:
keyevent("KEYCODE_DEL")
keyevent("67") # 67即为删除键,请注意传入的是字符串
模拟清空输入框操作:
for i in range(10):
keyevent("67")
poco的删除(输入框置空):
poco("xxx").set_text("")
。
log相关
如何记录log到报告中
log()
接口方便插入用户自定义的一些log信息,将会被显示在Airtest报告中。在1.1.6版本的Airtest中,log接口支持传入4个参数:
args
,可以是字符串、非字符串或者traceback
对象timestamp
,用于自定义当前log的时间戳desc
,用于自定义log的标题snapshot
,表示是否需要截取一张当前的屏幕图像并显示到报告中
示例如下:
# 传入字符串
log("123",desc="这是标题01")
# 传入非字符串
data = {"test": 123, "time": 123456}
log(data,desc="这是标题02")
# 传入traceback
try:
1/0
except Exception as e:
log(e, desc="这是标题03")
# 记录timestamp,并且对当前画面截图
log("123", timestamp=time.time(), desc="这是标题04", snapshot=True)
如何设置log的保存路径
Airtest提供了一些全局的设置,其中 LOGFILE
用于自定义记录log内容的txt文档的名称;LOGDIR
用于自定义log内容的保存路径,示例如下:
from airtest.core.settings import Settings as ST
from airtest.core.helper import set_logdir
ST.LOG_FILE = "log123.txt"
set_logdir(r'D:\test\1234.air\logs')
auto_setup(__file__)
如何过滤不必要的log信息
在脚本代码开头加上 对日志信息等级的设定:
__author__ = "Airtest"
import logging
logger = logging.getLogger("airtest")
logger.setLevel(logging.ERROR)
把输出日志信息的级别改成 [ERROR]
以后,整个脚本运行过程中只有少量初始化信息输出,更方便查看报错信息。
报告
报告生成:simple_report()
生成报告的简单接口:
simple_report(filepath, logpath=True, logfile='log.txt', output='log.html')
其中可传入的4个参数分别表示:
filepath
,脚本文件的路径,可以直接传入变量__file__
logpath
,log内容所在路径,如为True
,则默认去当前脚本所在路径找log内容logfile
,log.txt的文件路径output
,报告的到处路径,必须以.html
结尾
示例如下:
from airtest.report.report import simple_report
auto_setup(__file__, logdir=True)
# 此处省略N条用例脚本
simple_report(__file__,logpath=True,logfile=r"D:\test\1234.air\log\log.txt",output=r"D:\test\1234.air\log\log1234.html")
报告生成:LogToHtml()
报告的基类:
class LogToHtml(script_root, log_root='', static_root='', export_dir=None, script_name='', logfile='log.txt', lang='en', plugins=None)
logtohtml
类可以传入的参数非常多:
script_root
,脚本路径log_root
,log文件的路径static_root
,部署静态资源的服务器路径export_dir
,导出报告的存放路径script_name
,脚本名称logfile
,log文件log.txt的路径lang
,报告的语言(中文:zh;英文:en)plugins
,插件,使用了poco或者airtest-selenium会用到
使用 logtohtml
生成测试报告时,我们一般先实例化一个 logtohtml
对象,然后用这个对象调用类方法 report()
生成报告,示例如下:
from airtest.report.report import LogToHtml
# 此处省略N条用例脚本
h1 = LogToHtml(script_root=r'D:\test\1234.air', log_root=r"D:\test\1234.air\log", export_dir=r"D:\test\1234.air" ,logfile=r'D:\test\1234.air\log\log.txt', lang='en', plugins=["poco.utils.airtest.report"])
h1.report()
截图相关
如何用脚本截图
对目标设备进行一次截图,并且保存到文件中,可以传入截图文件名、截图的简短描述、截图压缩精度和截图最大尺寸,示例如下:
snapshot(filename="123.jpg",msg="首页截图",quality=90,max_size=800)
如何进行局部截图
局部截图或者说按坐标截图是大家经常会问到的问题,Airtest提供了 crop_image(img, rect)
方法可以帮助我们实现局部截图:
# -*- encoding=utf8 -*-
__author__ = "AirtestProject"
from airtest.core.api import *
# crop_image()方法在airtest.aircv中,需要引入
from airtest.aircv import *
auto_setup(__file__)
screen = G.DEVICE.snapshot()
# 局部截图
screen = aircv.crop_image(screen,(0,160,1067,551))
# 保存局部截图到log文件夹中
try_log_screen(screen)
如何做局部识图
局部找图的步骤:
进行局部截图
定义要查找的目标截图对象
利用
match_in
方法,在局部截图中查找指定的截图对象
from airtest.core.api import *
from airtest.aircv import *
auto_setup(__file__)
screen = G.DEVICE.snapshot()
# 局部截图
local_screen = aircv.crop_image(screen,(0,949,1067,1500))
# 将我们的目标截图设置为一个Template对象
tempalte = Template(r"png_code/设置.png")
# 在局部截图里面查找指定的图片对象
pos = tempalte.match_in(local_screen)
# 返回找到的图片对象的坐标(该坐标是相对于局部截图的坐标)
print(pos)
# 若要返回目标在整个屏幕中的坐标,则x,y都需要加上局部截图时设置的最小x、y
print(pos[0]+0,pos[1]+949)
如何设置报告的截图精度
SNAPSHOT_QUALITY
用于设置全局的截图压缩精度,默认值为10,取值范围[1,100]。示例如下:
from airtest.core.settings import Settings as ST
# 设置全局的截图精度为90
ST.SNAPSHOT_QUALITY = 90
定义单张截图的压缩精度,示例:
# 设置单张截图的压缩精度为90,其余未设置的将按照全局压缩精度来
snapshot(quality=90)
如何设置报告截图的最大尺寸
在Airtest1.1.6中,新增了一个用于指定截图最大尺寸的设置:ST.IMAGE_MAXSIZE
。假如设置为1200,则最后保存的截图长宽都不会超过1200,示例:
from airtest.core.settings import Settings as ST
# 设置全局截图尺寸不超过600*600,如果不设置,默认为原图尺寸
ST.IMAGE_MAXSIZE = 600
# 不单独设置的情况下,默认采用ST中的全局变量的数值,即600*600
snapshot(msg="test12")
# 设置单张截图的最大尺寸不超过1200*1200
snapshot(filename="test2.png", msg="test02", quality=90, max_size=1200)
如何指定截图保存的路径和名称
对当前设备的屏幕进行截图,并将截图保存在自定义路径下,可以用下述方式实现:
screen = G.DEVICE.snapshot()
pil_img = cv2_2_pil(screen)
pil_img.save(r"D:/test/首页.png", quality=99, optimize=True)
如何做断言
断言存在:assert_exists()
设备屏幕上存在断言目标,需要传入1个断言目标(截图)和在报告上显示的断言步骤信息,示例:
assert_exists(Template(r"tpl1607324047907.png", record_pos=(-0.382, 0.359), resolution=(1080, 1920)), "找到首页的天猫入口")
断言不存在:assert_not_exists()
设备屏幕上不存在断言目标,与 assert_exists()
一样,需要传入1个断言目标(截图)和在报告上显示的断言步骤信息,示例:
assert_not_exists(Template(r"tpl1607325103087.png", record_pos=(-0.005, 0.356), resolution=(1080, 1920)), "当前页不存在天猫国际的icon")
断言相等:assert_equal()
断言两个值相等,需要传入2个断言的值,还有将被记录在报告中的断言的简短描述:
assert_equal("实际值", "预测值", "请填写断言的简短描述")
常与poco获取属性的脚本一起做断言,示例如下:
assert_equal(poco("com.taobao.taobao:id/dx_root").get_text(), "天猫新品", "控件的text属性值为天猫新品")
assert_equal(str(poco(text="天猫新品").attr("enabled")), "True", "控件的enabled属性值为True")
断言不相等:assert_not_equal()
断言两个值不相等,与 assert_equal()
一样,需要传入2个断言的值,还有将被记录在报告中的断言的简短描述:
assert_not_equal("实际值", "预测值", "请填写断言的简短描述")
assert_not_equal("1", "2", "断言1和2不相等")
如何切换绝对坐标和相对坐标
用代码实现绝对坐标和相对坐标之间的切换:
# 获取设备屏幕分辨率(竖屏)
height = G.DEVICE.display_info['height']
width = G.DEVICE.display_info['width']
# 已知绝对坐标[311,1065],转换成相对坐标
x1 = 311/width
y1 = 1065/height
poco.click([x1,y1])
# 已知相对坐标[0.3,0.55],转换成绝对坐标
x2 = 0.3*width
y2 = 0.55*height
touch([x2,y2])
# 如果是横屏设备的话,则分辨率如下
height = G.DEVICE.display_info['width']
width = G.DEVICE.display_info['height']
判断当前屏幕为横屏还是竖屏,并获取当前屏幕的分辨率:
if G.DEVICE.display_info['orientation'] in [1,3]:
height = G.DEVICE.display_info['width']
width = G.DEVICE.display_info['height']
else:
height = G.DEVICE.display_info['height']
width = G.DEVICE.display_info['width']
如何调用别的.air脚本
如果想要在一个.air
脚本中,调用另外一个 .air
脚本里封装的公用函数,可以这样做:
from airtest.core.api import using
# 相对路径或绝对路径,确保代码能够找得到即可
using("common.air")
from common import common_function
common_function()
如果需要引用的子脚本路径统一都放在某个目录下,可以通过设定一个默认项目根目录 PROJECT_ROOT
,让使用 using
接口时能够在当前根目录下寻找别的子脚本,无需填写完整路径,让脚本之间相互调用使用更加方便。
例如,我们建立一个名为 test1.air
的脚本,实际路径为 /User/test/project/test1.air
:
from airtest.core.api import *
def test():
touch("tmp.png")
在同一目录下另外一个 main.air
脚本可以这样引用到它里面的 test
:
from airtest.core.api import *
ST.PROJECT_ROOT = "/User/test/project"
using("test1.air")
from test1 import test
如何在脚本运行过程中录制屏幕
目前仅支持Android设备的录屏,请参见在运行脚本过程中录屏