目前UI自动化测试目前主要分为元素定位、图像识别两种实现方式,其中元素定位方式为主流,图像识别方式为辅助方式。实际的自动化测试中,有的业务场景元素定位会比较困难,比如游戏类app、小程序页面、windows应用程序等,加上元素定位方式普遍存在的两个缺点:
- 元素位置定位&维护成本高
- 代码编写效率慢&入门有一定门槛&代码维护成本高
因此当元素定位方式不适合你的测试场景时,选用图片识别方式是另一条可行的测试方向。
那么今天我们主要分享一下如何基于Airtest+Python搭建一套适用于企业级可运行的自动化测试框架,希望对有这方面需求的同学有所帮助!
框架结构图
Airtest自带pywinauto以及多种图片识别技术,因此无需重复造轮子,是非常好用的图像识别自动化测试工具。加上Airtest基于python,可以较好的进行扩展和定制,因此可搭建Airtest+Python自动化测试框架。框架同时支持UI+接口自动化测试,UI界面操作完成后可继续进行服务端相关接口层面的验证,完成整个闭环。
框架结构图:
项目目录说明
项目目录说明:
Autotest
--config 存放配置文件
--global_config.py 用于配置全局的通用的变量,比如域名、登录账号等
--log 存放每个case单场景运行测试报告
--yyyy-mm-dd-hh-mm-ss_singleCaseName 用于存放每个case的详细测试报告文件
--resources 存放接口测试资源文件
--singleCaseName.json 单个case的接口测试资源Json文件
--testcases 存放测试用例文件
--singleCaseName.air 单个case的Airtest脚本文件夹
--utils 存放封装的通用工具类
--http_utils.py http接口封装request工具类
--read_json_file_utils.py json文件读取工具类
--common_utils.py 通用工具类
--report_template.py 整体测试报告生成工具类
--run.py 执行测试py文件, 包含逻辑:读取+运行case+生成报告
Python工程项目图:
其他说明:
Airtest IDE编写UI自动化测试脚本基本无需编程代码知识,上手编写脚本非常快,可快速完成大量测试脚本编写。关于如何提高脚本的稳定性、健壮性,以及使用中的一些技巧经验,可参看我之前的文章。
相关技术难点处理
难点1:Airtest IDE上编写的UI自动化测试脚本,每一个测试case是单独的一个.air文件夹,里面包含了.py的脚本文件和图片文件,每个脚本都是单独运行,如何将其批量运行并接入python框架?
解决方案:调研Airtest支持命令行模式运行,通过python代码发现框架下airtest脚本,调用cmd执行对应的命令,实现在python框架下批量运行脚本的功能。
def discover_test_cases(self):
"""
发现项目目录下所有的测试用例,以‘.air’后缀作为测试用例脚本作为判断依据
"""
test_cases = list()
for root, dirs, files in os.walk(self._test_case_dir):
paths = [os.path.join(root, d) for d in dirs if d.endswith(".air")]
test_cases.extend(paths)
return test_cases
def get_command(self, test_case):
"""
构造生成测试报告命令
"""
command = "airtest report {0} --log_root {1} --outfile {2} --lang {3}".format(test_case, self.log_root, self.outfile, self.lang)
return command
def run(self, test_case, log_root, outfile=None):
"""
执行生成测试报告命令
"""
self.log_root = log_root
self._outfile = outfile if outfile else ""
cmd = self.get_command(test_case)
os.system(cmd)
难点2:框架要实现一个测试脚本中既能执行该场景的UI自动化,又能执行该场景的接口自动化。UI自动化运行时采用的是Airtest框架,无法再使用接口自动化的unittest/pytest框架,怎样处理接口自动化的数据驱动和case运行管理机制?
解决方案:一次运行无法同时使用两套框架,因此不使用现有的unittest/pytest框架,使用python自研实现接口自动化需要的数据驱动和case运行机制:
- 数据驱动:通过resource文件夹下json文件管理接口测试数据入参和期望结果,通过json文件读取工具类实现接口入参和期望结果的获取。
- case运行机制:目前提供运行所有case、运行指定的某条case、运行指定优先级的case3种case运行方式。
@staticmethod
def partner_post_interface(file_path, file_name, cookies, domain_url):
# 获取文件相对路径
data_file_path = ReadJsonFileUtils.get_data_path(file_path, file_name)
# 读取测试数据文件
param_data = ReadJsonFileUtils(data_file_path)
# 是一个list列表,list列表中包含多个字典
data_item = param_data.get_value('dataItem')
# 循环获取list中的数据,根据数据发起多次测试
for i in data_item:
print("用例ID:{}".format(i['id']))
print("用例名称:{}".format(i['name']))
logging.info("测试开始啦~~~~~~~")
req_header = i['headers']
req_header['Cookie'] = cookies
res = HttpUtils.http_post(req_header, domain_url + i['url'], i['parameters'])
# 添加断言判断,判断接口是否返回期望的结果数据
# 循环获取expectdata中的所有的key和value,key是item[0],value是item[1]
for item in i['expectdata'].items():
# 拼接好jsonpath的路径格式
res_key_path = "$." + item[0]
# 使用jsonpath获取到值
res_key_value = jsonpath.jsonpath(res, res_key_path)[0]
print("res key:" + res_key_path + "," + "res value:" + str(res_key_value))
if str(res_key_value) == str(item[1]):
pass
else:
error_message1 = "接口返回值不等于预期值,\n" + "用例ID:" + str(i['id']) + ",\n"
error_message2 = "用例名称:" + str(i['name']) + ",\n"
error_message3 = "接口返回结果:" + str(res)
raise XError(error_message1 + error_message2 + error_message3)
难点3:Airtest框架每个case运行后都会生成单独的测试报告,批量运行多个case并不会生成汇总的一份测试报告,整体测试报告如何呈现?此外同一个脚本中包含UI自动化+接口自动化脚本时,接口自动化测试的结果如何在测试报告中展现?
解决方案:查看Airtest测试报告代码结构,通过python代码从每个case单独测试报告中提取出内容,整合数据编写一份新的定制化的整体测试报告。调研Airtest API源码,实现将接口自动化测试的函数运行结果也显示到Airtest报告中,通过抛出自定义异常,将接口自动化测试函数运行失败时的关键信息显示出来,方便排查定位问题原因。
def run_all_test_case(self):
test_cases = self.discover_test_cases()
print("发现测试用例共计{0}个:".format(len(test_cases)))
for test_case in test_cases:
print(test_case)
for test_case in test_cases:
print("开始执行测试用例:{0}".format(test_case))
self._runner.run(test_case)
print("完成执行测试用例")
print("开始生成用例报告")
log_path = self._runner.log_path
self._reporter.run(test_case, log_path)
data = self.get_airtest_log_data(self._reporter.outfile)
data["link"] = "/".join(self._reporter.outfile.replace(self.root_dir, "").strip(os.sep).split(os.sep))
data["title"] = data["info"]["title"] + " " + ".".join(data["name"].replace(self.root_dir, "").strip(os.sep).split(os.sep))
self._result.append(data)
print("完成生成用例报告")
template = ReportHtmlTemplateNew(self._result)
template.build()
report_file = template.report_file
print("汇总所有测试用例的测试报告:{0}".format(report_file))
测试结果效果图
整体测试报告效果图示例:
单个case脚本详细测试报告示例:
以上就是本次的全部内容,如果对你有帮助,欢迎关注+点赞+分享,你的支持就是作者更新最大的动力!