此文章来源于项目官方公众号:“AirtestProject”\
版权声明:允许转载,但转载必须保留原链接;请勿用作商业或者非法用途
在使用 Airtest 进行日常测试工作中,我们有时候想针对某个接口进行功能增强,或者增加一些错误处理和重试机制,但是又不想直接修改 Airtest 源码,因为担心更新 Airtest 库就会覆盖掉源码的修改。
这个时候,我们更推荐大家使用 python 覆盖函数的方式处理,通过覆盖函数的方式改变函数的行为是一种常见的编程技巧,既能让我们的代码保持灵活性,又避免了直接修改源码库。
下文我们将一起来看下覆盖函数在 Airtest 框架下的应用小案例.。
我们以覆盖 Airtest 里的touch
接口为例,来一起体验下覆盖函数的魅力:
新建一个new_touch
脚本文件,并将我们需要增强的函数或某个语句先导入并取一个与原函数不冲突的别名。
from airtest.core.api import touch as old_touch
这一行代码表示从 airtest.core.api
模块中导入了touch
函数,并将其重命名为old_touch
。这样做的目的是保留原有的touch
函数,以便在新函数中调用。
在新建的 **new_touch
** 文件下,可以正常调用旧函数并结合实际场景去增加所需要增强的功能,去实现想要达到的效果。
from airtest.core.api import *
from airtest.core.api import touch as old_touch
def touch(pic):
try:
#若打开App后是进入的学习积分界面,即可直接点击
old_touch(pic)
print("进入的是学习积分界面")
except TargetNotFoundError:
#若打开App后是进入的App首页界面,需要先点击积分
print("进入的是首页界面")
old_touch(Template(r"tpl1712044513750.png", record_pos=(0.267, -1.05), resolution=(1080, 2520)))
print("现在进入了学习积分界面")
#跳转到对应的学习积分界面后再进行点击
old_touch(pic)
创建一个新的跑测脚本,并将new_touch
文件放到与跑测脚本同一文件夹下,使用import
导入new_touch
文件的touch
用法,就可以直接使用我们修改后的touch
用法。
# -*- encoding=utf8 -*-
__author__ = "Airtest"
from airtest.core.api import *
from new_touch import touch
#打开进入学习强国App
start_app("cn.xuexi.android")
#尝试点击选读文章任务跳转
touch(Template(r"tpl1712044077691.png", target_pos=6, record_pos=(-0.006, 0.261), resolution=(1080, 2520)))
在需要使用新的touch
函数的脚本中,首先从airtest.core.api
导入所有内容,然后从定义了新touch
函数的模块(这里该模块名为new_touch
)导入新的touch
函数。由于 Python 的名称解析是从左到右的,后导入的同名函数会覆盖先前导入的函数,因此这样做可以确保新的touch
函数覆盖了原始的 touch
函数。
在了解完一个简单的覆盖函数示例之后,我们可以再来看一个稍微复杂点的小案例,就是利用覆盖函数,给旧函数增加一些错误处理操作或者错误 log 记录等。依然以 Airtest 的 touch
接口为例:
参考代码如下:
在新的touch
函数里,我们增加了一些错误记录,并截图错误场景,将报错抛出,在日常点击的时候可以更精准的去判断报错出现的场景以及原因。
from airtest.core.api import *
from airtest.core.api import touch as old_touch
import logging
# 初始化日志记录器
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
"""
param target: 点击目标,可以是图片(Template)或坐标。
param timeout: 超时时间,单位为秒。
param retry_interval: 重试间隔时间,单位为秒。
"""
def touch(target, timeout, retry_interval):
start_time = time.time() # 记录开始时间
while True:
try:
# 尝试点击目标
old_touch(target)
logger.info(f"成功点击图片")
break # 点击成功则退出循环
except TargetNotFoundError:
#计算超时时间
current_time = time.time()
elapsed_time = current_time - start_time
if elapsed_time > timeout:
# 如果超时,则捕获异常并记录错误信息
logger.error(f"在指定的超时时间 {timeout} 秒内未找到目标图片")
# 捕获屏幕截图以供后续分析
snapshot(filename=f"error_{current_time}.png")
raise # 重新抛出异常,或者可以选择其他的错误处理方式
else:
# 没有超时,则等待一段时间后重试
logger.info(f"未找到目标图片,{retry_interval} 秒后重试...")
time.sleep(retry_interval)
continue
except Exception as e:
# 捕获其他可能的异常,并记录
current_time = time.time()
logger.error(f"点击图片时发生未知错误: {e}")
# 捕获屏幕截图以供后续分析
snapshot(filename=f"error_{current_time}.png")
raise # 重新抛出异常,或者可以选择其他的错误处理方式
调用方式跟刚才简单的案例一样,从 enhanced_touch
文件中 import
新的touch
进来,需要注意 import
新 touch
的语句一定放在 from airtest.core.api import *
之后,确保覆盖。
# -*- encoding=utf8 -*-
__author__ = "Airtest"
from airtest.core.api import *
from enhanced_touch import touch
auto_setup(__file__)
start_app("cn.xuexi.android")
#调用修改后的touch,并传入需要识别的图片、超时时间、重试间隔时间
touch(Template(r"tpl1712113066466.png", record_pos=(0.298, 0.285), resolution=(1080, 2520)), timeout=10, retry_interval=5)
函数覆盖是一种强大的技术,可以在不改变原始代码的情况下调整、增强或替换现有功能。不过,这种技术也需要谨慎使用,因为它可能会导致代码的可读性和可维护性降低,尤其是在覆盖的函数逻辑变得复杂时。正确的文档记录和清晰的代码结构对于维护这种类型的代码至关重要。
我们的样例仅供参考,不一定适用于所有场合,同时也欢迎大家给我们投稿自己写的覆盖函数或封装函数,我们也会多多分享更多的相关的使用技巧。让我们一起努力,共同进步~
AirtestIDE 下载:airtest.netease.com/\
Airtest 教程官网:airtest.doc.io.netease.com/\
搭建企业私有云服务:airlab.163.com/b2b
官方答疑 Q 群:526033840