贵州整站优化seo平台,安徽网站建设公司,网站推广方案总结,如何去掉网站后缀wordpressAirtest 介绍
Airtest Project 是网易游戏推出的一款自动化测试框架#xff0c;其项目由以下几个部分构成
Airtest : 一个跨平台的#xff0c;基于图像识别的 UI 自动化测试框架#xff0c;适用于游戏和 App #xff0c; 支持 Windows, Android 和 iOS 平台#xff0c…Airtest 介绍
Airtest Project 是网易游戏推出的一款自动化测试框架其项目由以下几个部分构成
Airtest : 一个跨平台的基于图像识别的 UI 自动化测试框架适用于游戏和 App 支持 Windows, Android 和 iOS 平台 基于 Python 实现。
Poco : 一款基于 UI 组件识别的自动化测试框架目前支持 Unity3D , cocos2dx, Android 原生 App , iOS 原生 App 和 微信小程序 也可以在其他引擎中自行介入 poco-sdk 使用 基于 Python 实现
AirtestIDE : 提供一个跨平台的 UI 自动化测试编辑器 内置了 Airtest 和 Poco 的相关插件功能能够快速简单的编写 Airtest 和 Poco 代码
AirLab: 真机自动化云测试平台 目前提供 Top 100 手机兼容性测试海外云真机兼容性测试等服务
私有化手机集群技术 从硬件到软件 提供在企业内部私有化手机集群解决方案
总之Airtest 建立了一个比较完善的自动化测试方案我们利用它实现所见即所爬个人认为比 Appium 更加简单易用。本节我们先了解一下 AirtestIDE 的基本使用
准备工作
确保安装好 AirtestIDE , Airtest Python 库 和 Poco Python 库
只使用 AirtestIDE 实现自动化模拟和数据爬取也是没问题的 因为它里面已经内置了 Python 模块 Airtest Python 库和 Poco Python 库并且提供了非常便捷的可视化点选和代码生成等功能即使使用者没有任何 Python 基础 也能自动化控制 App 和完成数据爬取
但是对于需要爬取大量数据和控制页面跳转的场景而言 仅依靠可视化点选和自动生成代码来自动化控制 App , 其实是不灵活的。 进一步讲如果我们加入一些代码逻辑例如流程控制循环控制语句就可以爬取批量的数据了这时候需要依赖 Airtest, Poco 以及一些自定义逻辑和第三方库
Airtest 的官方文档 https://airtest.doc.io.netease.com/tutorial/1_quick_start_guide
详细介绍了 Airtest 的安装方式包括 AirtestIDE , Airtest Python 库和 Poco Python 库所以这里建议同时安装
下载路径 Airtest Project (netease.com)
下载后解压 然后打开 AirtestIDE.exe 会弹出一个登录提示跳过即可
安装完 AirtestIDE 之后 它还会安装一个 Python 环境 这个环境中附带安装了 Airtest Python 库和 Poco Python 库 不过这个被打包在 AirtestIDE 里面的环境和系统里安装的 Python 环境并不是同一个所以推荐直接使用 pip 工具将 Airtest Python 库和 Poco Python 库安装到系统环境中
pip install airtest
pip install pocoui
安装完成之后在 AirtestIDE 中把默认的 Python 环境由 AirtestIDE 附带的 Python 环境更换成系统的 Python 环境。
选项 ---- 设置 ---- 自定义 Python.exe 选择已有的 python 解释器即可 安装好之后需要准备一台 Android 真机 或者这模拟器真机需要通过 USB 线和电脑相连 确保 adb 能够正常连接到手机
AirtestIDE 体验
我这里使用的是夜神模拟器在 CMD 输入 adb devices 然后启动 AirtestIDE , 打开菜单中的 文件----新建脚本--- .air Airtest 项目 新建一个脚本选择一个路径将脚本命名为 script.air 之后点击确定 正常情况下已经链接上设备了如果没有连接刷新一下 我们可以点击页面中的屏幕对手机进行控制如果出现了链接问题可以参考官网
这时候可以点击 connect ,然后就可以看到手机屏幕了 至此要确保所有的步骤都成功了不然后面可能无法继续
我们来观察一下整个 AirtestIDE 页面分为 左 中 右 三个部分 一下内容为各组件介绍
左侧靠上的部分是 AirtestIDE 辅助窗 可以通过一些点选操作实现基于图像识别的自动化配置
左侧中间偏上的部分是 Poco 辅助窗 可以通过一些点选操作实现基于 UI 组件识别的自动化配置
中间靠上的部分是脚本编辑窗 即代码编辑区域可以通过 Airtest 辅助窗和 Poco 辅助窗自动生成代码也可以自己编写代码这个代码是基于 Python 语言的
中间靠下是 log 查看窗 即日志区域 会输出运行调试的一些日志
右侧是设备窗内容为手机屏幕用鼠标点击这个屏幕真机或模拟器的屏幕也会跟着变化而且响应速度很快
Airtest 的图像识别与自动化控制
Airtest 可以基于图像识别来自动化控制 App 本节我们就来体验一下这个功能。例如先点击作恶的 touch 按钮 意思是点击屏幕的某个位置 这时 AirtestIDE 会提示我们在右侧手机屏幕上截图这里我们截取的是 大众点评 的 APP 图标会发现 script.air 脚本中出现了一行代码 代码内容为 touch 方法 其参数是我们刚截取的图片 然后点击右侧的手机屏幕上的 “大众点评” 图标 进入这个 App , 再点击左侧的 wait 按钮 意思是等待指令内容加载出来之后同样根据提示截图 如截取首页左上角的美食图标 再点击左侧的 swipe 按钮 意思是滑动屏幕 这时 AirtestIDE 会提示我们框选一个位置 联想自己平时滑动屏幕的场景手指一开始先放在一个位置然后滑动到某个位置停止。那么这时的第一步需要框选的位置就是手指一开始需要放置的位置如 1 的地方 ---中间菜单栏 菜单栏下方加载的内容会变化 故选择相比之下更加通用不变的菜单栏作为识别目标 框选完毕后 AirtestIDE 会提示我们点选一个滑动目标位置这时选择点选上方的一个点即可 如果中标 2 的地方此时会发现 scrpt.air 脚本生成了一个 swipe 方法 其第一个参数是我们框选的菜单栏图片 第二个参数是一个 vector, 代表滑动的方向。
这样我们就通过一些可视化完成了自动化控制
最后我们再通过左侧的 keyevent 按钮添加两个键盘事件 在已经生成的代码开头和结尾分别加一个 HOME 键盘事件 代表进入首页和返回首页 先让鼠标停留在需要添加事件的位置然后点击 keyevent, 输入 HOME 开头和结尾都是一样
现在总结一下我们实现自动化控制的流程
1. 进入手机首页
2. 点击 “大众点评” App的图标
4. 等待左上角的 “美食” 图标加载出来
5. 向上滑动手机屏幕
6. 返回手机主页
接下来点击 script.air 脚本上方的运行按钮 三角按钮 会发现 AirtestIDE 可以驱动手机完成指定操作了和我们期望的一样点击等待 滑动操作顺序执行切 log 查看窗会显示执行的具体过程
以上便是 Airtest 提供的基于图像识别来自动化控制 App 的过程利用这项技术我们不用编写任何代码就可以让手机自动操作
其实 script.air 脚本内部对应的就是 Python 代码 只不过利用 AirtetIDE 封装了一层 使得编写和操作更加简单了。 我们可以追踪一下源码 在当前脚本的选项卡右击在弹出的菜单选项中选择“打开当前项目目录” 就会看到源码内容 可以看到其中有一个 Python 脚本 和 3 张刚才截取的图片打开 Python 脚本 可以看到其内容和 AirtestIDE 中自动生成的代码基本一致不同之处在于这里用了一个 Template 对象代替了图片 该对象包含图片名位置分辨率三个参数而 AirtestIDE 对图片进行了可视化 使其更加直观
我们可以更具Python 环境运行这个脚本么可以 但是需要在代码开始的 auto_setup(__file__) 和 keyevent(HOME) 之间添加一行代码 init_device() , 调用 init_device 方法的作用完成一些手机初始化配置 不做这一步可能会报错。 运行脚本后会产生同样的效果 手机会被自动化控制执行一系列的操作 同时控制台输出对应的操作日志
Airtest 的相关 API
上面的内容只是 Airtest Python 库的冰山一角本节列举一些它提供的便捷 API 。 从刚才的 init_device 方法说起这个方法是用来连接设备并初始化一些连接对象的。如果设置没有初始化则会先初始化设置并把初始化后的设备当作当前设备 def init-device(platformAndroid, uuidNone, **Kwargs) 用法示例 device: Android init_device(Android) print(device) airtest.core.android.android.Android object at 0x00000234FE2EC510 可以发现返回结果是一个 Android 对象 这个 Android 对象实际上属于 airtest.core.android 包 继承自 airtest.core.device.Device 类 与之并列的对象还有 airtest.core.ios.ios.IOS airtest.core.Linux.linux.Linux airtest.core.win.win.Windows 等。这些对象都有一些用来操作设备的 API 下面我们以 Android 对象的 API 为例总结一下
get_default_device : 获取默认设备
uuid : 获取当前设备 UUID
list_app : 列举设备上的所有 App
path_app 打印出某个 App的完整路径
check_app: 检查某个APP 是否在当前设备上
start_app : 启动某个 APP
stop_app : 停止某个 APP
start_app_timing : 启动某个 APP 并计算启动时间
clear_app : 清空某个 APP 的全部数据
install_app : 安装某个 APP
install_multiple_app : 安装多个 APP
uninstall_app : 卸载某个 APP
snapshot : 获取屏幕截图
shell : 获取 adb shell 命令执行结果
keyevent : 执行键盘操作
wake : 唤醒当前设备
home : 点击 HOME 键
text 向设备输入内容
touch : 点击屏幕上的某处
double_click :双击屏幕某处
swipe 滑动屏幕 由一点滑动至另一点
pinch 通过手指的捏合操作放大或缩小屏幕
logcat : 记录日志
getprop : 获取某个特定属性的值
get_ip_address : 获取 IP 地址
get_top_activity :获取当前 Activity
get_top_activity_name_and_pid : 获取当前 Activity 的名称和进程号
get_top_activity_name : 获取当前 Ativity 的名称
is_keyboard_shown : 判断当前是否显示键盘了
is_locked : 判断设备是否锁定了
unlock : 解锁设备
get_display_info : 获取当前显示信息 如屏幕宽高等
get_current_resolution 获取当前设备的分辨率
get_render_resolution : 获取当前渲染的分辨率
start_recording : 开始录制
stop_recording : 结束录制
adjust_all_screen : 调整屏幕的适配分辨率
下面做一些实例感受 from airtest.core.api import *
from airtest.core.android import Android
import logginglogging.getLogger(airtest).setLevel(logging.WARNING)# 初始化设备
device: Android init_device(Android)
# 是否上锁
is_locked device.is_locked()
print(fis_locked {is_locked})# 如果上锁 就解锁
if is_locked:device.unlock()
# 唤醒设备
device.wake()
# 列举设备上的 APP
app_list device.list_app()
print(fapp_list {app_list})
# 获取当前设备 UUID
uuid device.uuid
print(fuuid {uuid})
# 获取当前显示信息 如屏幕宽高等
display_info device.get_display_info()
print(fdisplay_info {display_info})
# 获取当前渲染的分辨率
resolution device.get_render_resolution()
print(fresolution {resolution})
# 获取 IP 地址
ip_address device.get_ip_address()
print(fip_address {ip_address})
# :获取当前 Activity
top_activity device.get_top_activity()
print(ftop_activity {top_activity})
# 判断当前是否显示键盘了
is_keyboard_shown device.is_keyboard_shown()
print(fis_keyboard_shown {is_keyboard_shown}) is_locked False app_list [com.android.cts.priv.ctsshim, com.android.providers.telephony ] uuid 127.0.0.1:62001 display_info {width: 720, height: 1280, density: 1.5, orientation: 0, rotation: 0, max_x: 720, max_y: 1280} resolution (0.0, 0.0, 720.0, 1280.0) ip_address 172.16.38.15 top_activity (com.android.launcher3, .launcher3.Launcher, 2509) is_keyboard_shown False 从结果可以看出 借助一些常用的 API 我们就完成了唤醒手机和获取 APP 列表 UUID 显示器信息 分辨率 IP地址 当前运行的 Activity 是否显示键盘等操作
获取当前设备
Airtest 中有一个全局变量 G 它的 DEVICE 属性代表当前的设备对象。这直接调用 device 方法即可获取当前设备该方法定义如下 def device(): return G.DEVICE 获取所有设备 print(G.DEVICE_LIST)
uri Android://127.0.0.1:5037/127.0.0.1:62001
device: Android connect_device(uri)
print(G.DEVICE_LIST) [] [airtest.core.android.android.Android object at 0x0000028A8B6CE250] uri 获取 DEVICE_LIST 是一个列表元素是 Airtest 当前已经连接的设备需要注意的是在没有调用 connect_device 方法的时候 DEVICE_LIST 是空的 调用 connect_device 方法后DEVICE_LIST 会自动添加已经连接的设备
执行命令
可以调用 shell 方法传入 cmd 参数来执行命令行直接调用 adb 命令就可以了 logwrap
def shell(cmd):return G.DEVICE.shell(cmd)
uri android://127.0.0.1:5037/127.0.0.1:62001
connect_device(uri)
result shell(cat /proc/meminfo)
print(result)MemTotal: 3566528 kB MemFree: 3061388 kB MemAvailable: 3285952 kB Buffers: 420 kB Cached: 280936 kB SwapCached: 0 kB Active: 259808 kB Inactive: 210492 kB 这样就获取了设备的内存信息
启动和停止
调用设备的 start_app 和 stop_app 方法然后传入 App 的包名即可启动和停止这个 App logwrap
def start_app(package, activityNone):G.DEVICE.start_app(package, activity)logwrap
def stop_app(package):G.DEVICE.stop_app(package)uri android://127.0.0.1:5037/127.0.0.1:62001
connect_device(uri)
package com.goldze.mvvmhabit
start_app(package)
sleep(10)
stop_app(package) 这里指定了 package 为 app5的包名然后调用 start_app 方法启动了 app5, 等待 10 秒后 调用 stop_app 方法停止了 app5 的运行
安装和卸载 app
调用设备的 install 和 uninstall 方法 前者传入 APP 的保存路径后者传入包名 即可安装和卸载 app # 安装 app logwrap def install(filepath, **kwargs): return G.DEVICE.install_app(filepath, **kwargs) # 卸载 app logwrap def uninstall(package): return G.DEVICE.uninstall_app(package) 截图
利用 snapshot 方法获取屏幕截图 可以通过参数设置存储截图的文件名称和图片的质量等 def snapshot(filenameNone, msg , quality ST.SNAPSHOT_QUALITY) 用法示例 uri android://127.0.0.1:5037/127.0.0.1:62001
connect_device(uri)
package com.goldze.mvvmhabit
G.DEVICE.start_app(package)
sleep(3)
snapshot(app5.png, quality30)
G.DEVICE.stop_app(package) 唤醒和回到首页
调用设备的wake 和 home 方法即可唤醒 APP 和回到首页两个方法的定义如下 # 唤醒 logwrap def wake(): G.DEVICE.wake() # 回到首页 logwrap def home(): G.DEVICE.home() 这两个方法不需要参数直接调用即可
点击屏幕
调用 touch 方法点击屏幕 可传入要点击的图片或绝对位置还可以指定点击次数声明如下 logwrap def touch(v, times1, **kwargs) 例如我们从手机上截一张需要点击的 app 的图片然后把这张图片声明成一个 Template 对象传入 touch 方法 uri android://127.0.0.1:5037/127.0.0.1:62001
connect_device(uri)
touch(Template(app.png)) 运行这段代码后设备就会启动然后点击这张图片
我们也可以传入绝对位置具体位置以自己设备为准需要测量 uri android://127.0.0.1:5037/127.0.0.1:62001
connect_device(uri)
home()
touch((70, 645)) 另外touch 完全等同于 click 方法如果需要双击可以调用 double_click 方法等同于 touch 的 times 2 , click 方法的参数和 touch 的参数是一样的
滑动
调用 swipe 方法滑动屏幕 可以传入起始位置和结束位置两个位置都可以是图片或者绝对位置
声明如下 logwrap def swipe(v1, v2None, vectorNone, **kwargs) 例如我们想要控制手机向右滑动可以实现如下代码 uri android://127.0.0.1:5037/127.0.0.1:62001
connect_device(uri)
home()
swipe((200, 300), (900, 300)) 放大缩小
放大缩小是调用 pinch 方法 可以通过 in_or_out 参数指定放大还是缩小还可以指定手指捏合的中心点位置和放大缩小的比例 该方法的声明如下 logwrap def pinch(in_or_outin, centerNone, percent0.5) 用法如下 uri android://127.0.0.1:5037/127.0.0.1:62001
connect_device(uri)
home()
pinch(in_or_outout, center(300, 300), percent0.4) 这里我们调用了 pinch 方法 并且制定了放大动作 out 同时指定了捏合的中心点和捏合比例
键盘事件
调用 keyevent 方法来按下某个键 例如 HOME 键返回键等。声明如下 def keyevent(keyname, **kwargs) 用法示例
keyevent(HOME)
表示按下HOME 键
输入内容
调用 text 方法来输入内容前提是目标 Widget 需要处于 active 状态。声明如下 logwrap def text(text, enterTrue, **kwargs) 调用该方法后目标 Widget 就会输入相应的字符输入完之后会执行一次确认按回车键
基于 Poco 的 UI 组件自动化
在某些场景下基于图像的识别来自自动化控制 App 是比较方便的但也存在一定的局限性。例如图像识别速度可能不快以及 App 中的某些 UI 如果更换了就无法和之前截图的图片匹配成功这些很可能影响自动化测试流程
所以这里再介绍一些基于 Poco 的 UI 组件自动化控制说白了就是基于 UI 名称和属性选择器的自动化控制有点类似于 Appium, Selenium 中的 XPath
新建一个脚本命名为 script2.air , 右侧同样连接好手机然后点击左侧 Poco 辅助窗 选择 Android 这时会提示我们更新代码点击确定后脚本中自动添加了代码 from poco.drivers.android.uiautomation import AndroidUiautomationPoco poco AndroidUiautomationPoco(use_airtest_inputTrue, screenshot_each_actionFalse) 意思是导入了 Poco 包的 AndroidUiautomationPoco 模块然后声明了一个 poco 对象。接下来就可以通过 poco 对象选择一些内容了。 例如点击左侧 UI 组件树中的 “大众点评“ 节点就会发现手机右侧屏幕上对应的 app 高亮显示了在 Log 查看窗 还可以看到该节点对应的所有属性。这个操作有点像在浏览器开发者工具中选取网页源代码 其中的 UI 组件树就相当于网页里的 HTML DOM 树 直接双击 “大众点评” 节点 script2.air 脚本就会出现对应的代码 poco(大众点评) 我们来看一下 poco 的 API 这是一个 AndroidUiautomationPoco 对象
官方文档 https://poco.readthedocs.io/zh_CN/latest/source/poco.pocofw.html
其用法类似如下 poco AndroidUiautomationPoco(...) close_bin poco(close, typeButton) 会发现 Poco 本身就是一个对象但可以直接调用 UI 组件的名称 这归根结底是因为实现了一个 __call__ 方法 def __call__(self, nameNone, **kw): if not name and len(kw) 0: warnings.warn(Wildcard selector may cause performance trouble, Please give at least one condition to shrink range of results) return UIObjiectProxy(self, name, **kw) 可以看到 __call__ 方法第一个参数是 name 其他参数都以 kw 的形式传入可以任意指定 最后返回一个 UIObjectProxy 对象
回过头来 我们看看 “大众点评” 这个节点的 name 参数值是什么 这个在 Log查看窗 内显示的很清楚 可以看到其 name 就是 “大众点评” 而且整个 UI 树没有与其同名的节点 于是可以直接调用 poco(大众点评) 选取这个节点当然也可以任意指定 poco 的其他参数 poco(大众点评, typeandroid.widget.TextView) poco(大众点评, text大众点评) poco(大众点评, text大众点评, desc大众点评) 这三种方法都能选取同样的节点
刚才说到 __call__ 会返回一个 UIObjectProxy 对象 现在我们来看一下这个对象的实现其 API 链接为 https://poco.readthedocs.io/zh_CN/latest/source/poco.pocofw.html 从中可以看出它实现了 __getitem__ , __iter__, __len__ , child, children , offspring 等方法所以可以实现链式调用索引操作和循环遍历
其中一些比较常用的方法如下
child : 选择子节点。 第一个参数是 name , 即 UI 组件的名称 如 android.widget.LinearLayout 等 还可以传入一些属性辅助选择 其返回结果也是 UIObjectProxy 对象
perent: 选择父节点。 该方法无需传入参数 可以直接返回当前节点的父节点返回同样是 UIObjectProxy 对象
sibling: 选择兄弟节点。 第一个参数是 name , 即 UI 组件的名称 同样可以额外传入一些属性辅助选择返回结果依然是 UIObjectProxy 对象
click, rclick , double_click, long_click : 分别是点击 右击双击 长按。 UIObjectProxy 对象可以直接调用这几个方法 参数 focus 用于指定点击的偏移量 sleep_interval 用于指定点击完成后的等待时间 单位为 秒
swipe :滑动操作。参数 direction 用于指导滑动方向 focus 用于指导滑动焦点的偏移量 duration 用于指导完成滑动所需的时间
wait , wait_for_appearance 等待某节点的出现。 参数 timeout 用于指定最长等待时间
attr : 获取节点的属性值 参数 name 用于指定要获取的属性名 如 visable , text , type, pos, size 等
get_text: 获取节点的文本值。 这个方法非常有用 可以获取某个文本节点内部的文本数据。
下面调用 click 方法 将代码改为 poco(大众点评).click() 这样就可以选中并点击 “大众点评” 节点 点击之后就进入 “大众点评” 这个 App 然后可以设置一下等待条件 等待某个节点加载出来 证明已经进入 App 了 然后点击 左侧的 Poco Pause 按钮 可以在右侧屏幕上点击想要查看的位置左侧 UI 组件树就会自动定位到对应的节点 同时 Log查看窗 会实时显示节点信息 双击左侧 UI 组件树中定位到的节点 script2.air 中又会增加如下内容 poco(com.dianping.v1:id/home_category_layout) 然后可以在后面加上等待时间 poco(com.dianping.v1:id/home_category_layout).wait_for_appearance(10) 代表等待 10 秒如果加载不出来就报错
同样可以选中中间菜单栏的位置向上滑动 poco(desc美食).swipe([0, -0.1]) 这里往 swipe 方法的参数传入一个列表 代表滑动方向 列表第一个元素代表横向偏移量第二个元素代表纵向偏移量 由于我们要向上滑动 因此第一个元素是 0 第二个元素是 -0.1
最后在代码开头和结尾添加键盘事件回到首页整理代码如下 # -*- encodingutf8 -*- __author__ 86151 from airtest.core.api import * auto_setup(__file__) from poco.drivers.android.uiautomation import AndroidUiautomationPoco poco AndroidUiautomationPoco(use_airtest_inputTrue, screenshot_each_actionFalse) keyevent(HOME) poco(大众点评).click() poco(com.dianping.v1:id/home_category_layout).wait_for_appearance(25) poco(desc美食).swipe([0, -0.1]) keyevent(HOME) 运行这段代码之后手机上就会先进入桌面 然后点击 “大众点评” 图标进入 APP 等待相应内容加载出来之后 向上滑动 最后返回桌面