Pytest中实现自动生成测试用例脚本代码

🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快

在Python的测试框架中,我们通常会针对某个系统进行测试用例的维护,在对庞大系统进行用例维护时,往往会发现很多测试用例是差不多的,甚至大多数代码是一样的。

故为了提高我们测试用例维护的效率,在本文中,我们将探讨如何自己设计脚本,自动生成测试用例脚本。

自动发现测试原理

pytest的一个重要特性就是它可以自动发现并运行测试。所以我们只需要按照一定的命名规则来命名测试文件和测试函数,然后pytest就可以自动找到并运行这些测试。

默认情况下,pytest会运行所有名称以test_开头或者以_test结尾的.py文件中的测试。在这些文件中,pytest会运行所有名称以test_开头的函数或者方法。

例如,以下代码定义了一个测试函数:

def test_addition():assert 1 + 1 == 2

你可以把这个函数保存在一个名为test_example.py的文件中,然后在命令行中运行pytest命令,pytest就会自动找到并运行这个测试。

接口测试数据维护

为了达到目的,所以我们约定以特定规则进行接口测试数据的维护,我们选择json类型文件进行维护,文件目录结构如下:

需要注意的是,考虑到维护的便利性,我们推荐根据模块进行分层管理接口数据文件。

读取接口测试文件信息方法

有了接口数据文件,那么我们就可以设计一个方法,根据传入的接口模块、接口名称,进行相应的接口文件地址拼接,从而去读取json文件内容,代码如下:

class GetTestData:"""获取测试数据封装模块"""def __init__(self, logger):self.test_data_path = FilePathSetting.test_data_path  # 获取测试数据配置路径self.logger = loggerdef get_api_data(self, api_module, api_name):"""根据接口模块和接口名称 获取接口配置信息:param api_module:接口模块:param api_name:接口名称:return:"""# 拼接需要读取接口模板信息的*.json文件路径地址api_data_path = os.path.join(os.path.join(self.test_data_path, api_module), api_name)self.logger.info("{init_api_data} 接口模板文件地址:%s" % api_data_path)# 读取接口模板路径下的*.json信息with open(file=api_data_path, mode='r', encoding='utf-8') as f:api_data = json.load(f)self.logger.info("{init_api_data} 接口模板信息:%s" % json.dumps(api_data, ensure_ascii=False))return api_datadef get_case_data(self, pytest_current_test):"""根据传入的test_*.py路径 获取case配置信息:param pytest_current_test::return:"""# self.logger.info('~~~~~~~~~~~~ {init_api_data} 初始化当前接口请求模板数据 ~~~~~~~~~~~~')self.logger.info('{init_api_data} 当前test_case位置信息:%s' % pytest_current_test)# 根据传入的test_*.py路径 获取接口所属模块文件名称file_path = pytest_current_test.split("::Test")[0]self.logger.info('{init_api_data} 当前接口test_case文件地址:%s' % file_path)# 根据传入的test_*.py路径 获取接口所属模块文件名称api_module = os.path.split(os.path.split(file_path)[0])[1]self.logger.info("{init_api_data} 接口所属模块:%s" % api_module)# 根据传入的test_*.py路径 获取接口模板(*.json)名称# # api_name = os.path.split(file_path)[1].replace('.py', '.json').replace('test_', '')api_name = os.path.split(file_path)[1][5:].replace('.py', '.json')  # 1、截取第6个字符到结尾;2、将.py后缀替换为.jsonself.logger.info("{init_api_data} 接口模板文件名称:%s" % api_name)return self.get_api_data(api_module, api_name)

基础的测试用例模板

因为我们基础的测试用例是通用的,如以下代码实例:

import allure
import pytestfrom utils.assertion.assert_controller import AssertController
from utils.requests_tools.requests_client import RequestClientapi_module = '接口模块***'
api_name = '接口名称***'@allure.epic(api_module)
@allure.feature(api_module + '' + api_name)
class TestCaseGroup:@allure.title("接口健康检查")@allure.description("接口返回信息基础验证")@pytest.mark.smokedef test_smoke(self, init_api_data):# 获取环境配置信息&接口测试数据env_data, case_data = init_api_data# 获取接口返回信息res = RequestClient(case_data).get_api_response()# 进行断言验证AssertController(case_data.assert_data).assert_equality(res.response_data)

故我们就可以设计一个用例模板的生成器:

import datetime
from typing import Textdef write_case(file_path: Text, page: Text) -> None:""" 写入用例数据 """with open(file_path, 'w', encoding="utf-8") as file:file.write(page)def write_init_file(*, file_path: Text) -> None:"""初始化文件写入:param file_path: __init__文件地址:return:"""now = datetime.datetime.now().strftime('%Y/%m/%d %H:%M')page = f'''#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author    : gmluo1988
# @Software  : PyCharm
# @File      : __init__.py.py
# @Time      : {now}
'''write_case(file_path=file_path, page=page)def write_test_case_file(*,api_module: Text,api_name: Text,filename: Text,case_path: Text) -> None:"""标准测试用例文件写入:param api_module: 接口所属模块:param api_name: 接口名称名称:param filename: 用例文件名称:param case_path: 用例文件地址"""now = datetime.datetime.now().strftime('%Y/%m/%d %H:%M')page = f'''#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Author    : gmluo1988
# @Software  : PyCharm
# @File      : {filename}
# @Time      : {now}
import allure
import pytest
from utils.assertion.assert_controller import AssertController
from utils.requests_tools.requests_client import RequestClient
@allure.epic("{api_module}")
@allure.feature("{api_module} {api_name}")
class TestCaseGroup:@allure.title("接口健康检查")@allure.description("接口返回信息基础验证")@pytest.mark.smokedef test_smoke(self, init_api_data):# 获取环境配置信息&接口测试数据env_data, case_data = init_api_data# 获取接口返回信息res = RequestClient(case_data).get_api_response()# 进行断言验证AssertController(case_data.assert_data).assert_equality(res.response_data)
'''write_case(file_path=case_path, page=page)

有了测试用例生成器,那么我们就可以根据接口模块,接口名称,以及接口相关信息生成通用的接口用例文件了。

自动生成测试用例

接下来要做事情就是读取test_data目录下所有模块下的接口文件,遍历文件列表进行测试用例的字段生成。

要注意的是:

1、该封装方法可以根据需求设置需要排除的文件列表

2、test_case目录下已经存在的用例会跳过生成

3、该封装方法生成的是一个基本通用的用例,可在生成的用例基础上进行改造

import osfrom common.file_path_setting import FilePathSetting
from utils.case_automatic.test_case_template import write_test_case_file, write_init_file
from utils.get_test_data_tools.get_case_data import GetCaseDataclass CaseAutoGenerate:"""测试用例自动生成工具"""def __init__(self):"""设置需要排除的文件列表,一般是Cloud登录以及子系统跳转登录等前置条件接口文档"""self.excluded_file_list = [("users", "login.json"),("users", "change_password.json")]def get_test_data_files(self) -> list:"""获取所有测试数据文件路径信息列表每条测试数据由一个tuple组成,包含3个值(filename:文件名;parent_dir:父级目录名称;file_path:文件地址)需要排除不需要自动生成测试用例的文件:return:测试数据文件路径信息列表"""file_info_list = []  # 所有测试数据文件路径信息列表# 获取测试数据目录所有文件下的子文件名称for root, dirs, filenames in os.walk(FilePathSetting.test_data_path):for filename in filenames:parent_dir = os.path.basename(root)  # 测试数据文件父级目录名称file_path = os.path.join(root, filename)  # 测试数据文件地址# 需要排除不需要自动生成测试用例的文件if (parent_dir, filename) not in self.excluded_file_list:# 每条测试数据由一个tuple组成,包含3个值(filename:文件名;parent_dir:父级目录名称;file_path:文件地址)file_info_list.append((filename, parent_dir, file_path))# 返回所有测试数据文件路径信息列表return file_info_list@staticmethoddef get_test_case_files() -> tuple:"""获取所有测试用例父级目录列表,测试用例文件地址列表需要将'__init__.py', 'conftest.py'文件进行过滤:return:"""case_dir_list = []  # 用例父级目录列表case_path_list = []  # 测试用例文件地址列表# 获取测试用例目录下所有文件下的子文件名称for root, dirs, filenames in os.walk(FilePathSetting.test_case_path):for filename in filenames:parent_dir = os.path.basename(root)file_path = os.path.join(root, filename)# 需要将'__init__.py', 'conftest.py'文件进行过滤if filename not in ['__init__.py', 'conftest.py']:case_path_list.append(file_path)# 如果父级目录还未收录,则进行父级目录列表更新,并将'__pycache__'排除if parent_dir not in case_dir_list and parent_dir not in ['__pycache__']:case_dir_list.append(parent_dir)# 分别返回测试用例父级目录列表,测试用例文件地址列表return case_dir_list, case_path_list@staticmethoddef mk_dir(test_data_file_info: tuple, existing_case_dir_list: list) -> None:"""创建测试用例父级目录如果测试数据文件的父级目录在测试用例目录中是不存在,则在test_case目录下创建相同名称的package文件以及'__init__.py'文件:param test_data_file_info: 测试数据文件信息,包含3个值(filename:文件名;parent_dir:父级目录名称;file_path:文件地址):param existing_case_dir_list: 已有的所有测试用例父级目录列表:return:"""test_data_dir = test_data_file_info[1]  # 测试数据文件,父级目录名称# 如果测试数据文件的父级目录在测试用例目录中是不存在,则在test_case目录下创建相同名称的package文件以及'__init__.py'文件if test_data_dir not in existing_case_dir_list:print("test_case下缺少:{} 文件目录,进行文件新增操作...".format(test_data_dir))"""创建相同名称的package文件"""dir_path = os.path.join(FilePathSetting.test_case_path, test_data_dir)os.makedirs(dir_path, exist_ok=True)"""新增'__init__.py'文件"""init_file_path = os.path.join(dir_path, '__init__.py')# open(init_file_path, 'w').close()write_init_file(file_path=init_file_path)@staticmethoddef is_case_exist(test_data_file_info: tuple, existing_case_path_list: list) -> tuple:"""判断测试用例是否存在如果不存在,则返回需要生成测试用例的文件名和文件地址信息如果已经存在对应的测试用例,则返回None:param test_data_file_info::param existing_case_path_list::return:target_case_filename, target_case_file_path"""test_data_filename = test_data_file_info[0]  # 测试数据文件名称test_data_dir_name = test_data_file_info[1]  # 测试数据文件所属父级目录名称# 拼接目标测试用例所属父级目录地址target_case_dir_path = os.path.join(FilePathSetting.test_case_path, test_data_dir_name)# 组装目标测试用例文件名称:测试用例以“test_”开头,将文件格式“.json”替换成“.py”target_case_filename = 'test_' + test_data_filename.replace('.json', '.py')# 拼接目标测试用例文件地址target_case_file_path = os.path.join(target_case_dir_path, target_case_filename)# if target_case_file_path in existing_case_path_list:#     print(target_case_file_path)if target_case_file_path not in existing_case_path_list:# 返回需要生成测试用例的文件名和文件地址信息return target_case_filename, target_case_file_pathelse:return None, Nonedef case_automatic(self) -> None:"""测试用例自动生成入口:return:""""""获取所有测试数据文件路径信息列表"""test_data_file_info_list = self.get_test_data_files()"""获取所有测试用例父级目录列表,测试用例文件地址列表"""existing_case_dir_list, existing_case_path_list = self.get_test_case_files()"""遍历每个测试数据文件,如果对应的测试用例不存在,则进行测试用例自动生成操作"""for test_data_file_info in test_data_file_info_list:"""创建测试用例父级目录"""self.mk_dir(test_data_file_info, existing_case_dir_list)"""返回需要生成测试用例的文件名和文件地址信息"""target_case_filename, target_case_file_path = self.is_case_exist(test_data_file_info,existing_case_path_list)"""创建对应的测试用例文件"""if target_case_filename and target_case_file_path:print("测试用例文件:{} 不存在,进行测试文件新增...".format(target_case_file_path))test_data_file_path = test_data_file_info[2]case_data = GetCaseData().get_api_data_by_path(test_data_file_path)api_module = case_data.api_moduleapi_name = case_data.api_namewrite_test_case_file(api_module=api_module,api_name=api_name,filename=target_case_filename,case_path=target_case_file_path)if __name__ == '__main__':print("Generating test case...")case_auto_generate = CaseAutoGenerate()case_auto_generate.case_automatic()

总结

Python中通过自己设计脚本,从而能够自动生成测试用例脚本,提高我们测试用例维护的效率,当然不同的系统可能有自己特别的方式,在此是为大家作个参考。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/92685.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/92685.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一周学会Matplotlib3 Python 数据可视化-标注 (Annotations)

锋哥原创的Matplotlib3 Python数据可视化视频教程: 2026版 Matplotlib3 Python 数据可视化 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 课程介绍 本课程讲解利用python进行数据可视化 科研绘图-Matplotlib,学习Matplotlib图形参数基本设置&…

安全合规1--实验:ARP欺骗、mac洪水攻击、ICMP攻击、TCP SYN Flood攻击

一、实验环境 (思科的云实验平台)攻击机:Kali Linux(IP:192.168.234.128,MAC:00:00:29:35:64:EC)目标1:网关(IP:192.168.234.2,MAC:00:50:56:ED:D…

Linux下GCC的C++实现Hive到Snowflake数据迁移

程序结构 ├── main.cpp ├── config.json ├── hive_export/ ├── parquet_data/ ├── sql_scripts/ └── logs/核心代码实现 (main.cpp) #include <iostream> #include <fstream> #include <vector> #include <thread> #include <mut…

drippingblues靶机教程

一、信息搜集首先将其在VirtualBOX中安装&#xff0c;并将kali与靶机都设置为桥接模式紧接着我们扫描IP&#xff0c;来发现靶机地址&#xff0c;经过搜集&#xff0c;发现IP是192.168.1.9&#xff0c;我们去访问一下紧接着我们扫一下开放了哪些端口。发现开放了21、22以及80端口…

39.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--扩展功能--调整发布脚本

这篇文章&#xff0c;我们要调整发布脚本。之所以要调整发布脚本&#xff0c;是因为现在我们的项目有三个环境&#xff1a;本地&#xff08;Local&#xff09;、开发&#xff08;Development&#xff09;、生产&#xff08;Production&#xff09;。Tip&#xff1a;我们的项目虽…

商品、股指及ETF期权五档盘口Tick级与分钟级历史行情数据多维解析

在金融数据分析领域&#xff0c;本地CSV文件是存储高频与低频数据的常用载体。本文以期权市场数据为例&#xff0c;探讨如何基于CSV格式处理分钟级行情、高频Tick数据、日频数据、逐笔委托记录、五档订单簿及历史行情数据&#xff0c;并提供专业的技术实现方案。以下将从数据预…

云端软件工程智能代理:任务委托与自动化实践全解

云端软件工程智能代理&#xff1a;任务委托与自动化实践全解 背景与未来趋势 随着软件工程复杂度不断提升&#xff0c;开发者对自动化工具的依赖也日益增强。我们正进入一个“人机协作”的新时代&#xff0c;开发者可以专注于核心创新&#xff0c;将重复性、繁琐的任务委托给智…

making stb style lib(1): do color print in console

col.h: see origin repo // origin repo: https://github.com/resyfer/libcol #ifndef _COL_HOL_H_ #define _COL_HOL_H_#include <stdlib.h> #include <stdio.h> #include <stdbool.h> #include <string.h> #include <math.h> // 新增&#xf…

llm本地部署+web访问+交互

要实现基于llm的web访问和交互&#xff0c;需支持对llm的访问和对网络搜索的调用。 这里使用ollama llm兼容openai sdk访问&#xff1b;使用proxyless-llm-websearch模拟网络搜索。 1 ollama本地部署 假设ollama已经部署&#xff0c;具体过程参考 在mac m1基于ollama运行dee…

自动驾驶数据闭环

自动驾驶的数据闭环是支撑算法持续迭代的核心机制&#xff0c;其本质是通过“数据采集-处理-训练-部署-反馈”的循环&#xff0c;不断优化模型对复杂场景的适应性。由于自动驾驶数据量极大&#xff08;单车日均TB级&#xff09;、场景多样&#xff08;从常规道路到极端边缘场景…

二十、MySQL-DQL-条件查询

DQL-条件查询代码&#xff1a; DQL-条件查询 -- 1.查询 姓名 为 杨逍 的员工 select * from tb_emp where name 杨逍; -- 2.查询 id小于等于5 的员工信息 select * from tb_emp where id < 5; -- 3.查询 没有分配职位 的员工信息 select * from tb_emp where job is null; …

Mac下安装Conda虚拟环境管理器

Conda 是一个开源的包、环境管理器&#xff0c;可以用于在同一个机器上创建不同的虚拟环境&#xff0c;安装不同Python 版本的软件包及其依赖&#xff0c;并能够在不同的虚拟环境之间切换 Conda常通过安装Anaconda/Miniconda来进行使用。一般使用Miniconda就够了。Miniconda 是…

Android 中解决 Button 按钮背景色设置无效的问题

1、问题描述 在布局文件中有两个 Button 按钮&#xff0c;为每个按钮设置不同的背景色&#xff0c;但是显示出来的效果都是紫色的&#xff0c;跟设置的颜色不同&#xff0c;布局文件如下所示&#xff1a;<Buttonandroid:id"id/button_cancel"android:layout_width…

云服务器--阿里云OSS(2)【Springboot使用阿里云OSS】

&#x1f4d2; 阿里云 OSS Spring Boot 异步任务&#xff08;直接存 OSS&#xff09; 1. 项目结构 src/main/java/com/example/demo├── controller│ └── UploadController.java // 接收上传请求├── service│ ├── AsyncUploadService.java // 异步上传…

get请求中文字符参数乱码问题

第一种方法 服务器默认的传参编码格式是ISO8859-1,所以前端直接原样字符串请求&#xff0c;到后端解析一下就得到正确字符 String fileName request.getParameter("fileName"); fileName new String(fileName.getBytes("ISO8859-1"),"UTF-8");…

C语言(10)——结构体、联合体、枚举

关于C语言零基础学习知识&#xff0c;小编有话说&#xff0c;各位看官敬请入下面的专栏世界&#xff1a;打怪升级之路——C语言之路_ankleless的博客-CSDN博客 Hi&#xff01;冒险者&#x1f60e;&#xff0c;欢迎闯入 C 语言的奇幻异世界&#x1f30c;&#xff01; 我是 Ankle…

海康威视摄像头实时推流到阿里云公网服务器(Windows + FFmpeg + nginx-rtmp)

海康威视摄像头实时推流到阿里云公网服务器&#xff08;Windows FFmpeg nginx-rtmp1. 步骤总览2. 阿里云 ECS&#xff08;Linux&#xff09;配置2.1 开放端口2.2 安装 nginx-rtmp3. Windows 电脑端配置3.1 安装 FFmpeg3.1.1 官网/镜像下载&#xff1a;3.1.2 解压后将 bin 目录…

基础网络网路层——IPV4地址

在IP网络上&#xff0c;如果用户要将一台计算机连接到Internet上&#xff0c;就需要向因特网服务提供方ISP&#xff08;Internet Service Provider&#xff09;申请一个IP地址。IP地址是在计算机网络中被用来唯一标识一台设备的一组数字。IPv4地址由32位二进制数值组成&#xf…

技术速递|GPT-5 正式上线 Azure AI Foundry

AI 应用正在经历一场深刻变革——对企业来说&#xff0c;仅仅“能聊天”早已不够&#xff0c;生成内容、逻辑推理、落地生产&#xff0c;这些才是新时代对 AI 能力的真正考验。 今天&#xff0c;我们非常激动地宣布&#xff0c;OpenAI 最新旗舰大模型 GPT-5 正式上线 Azure AI …

Logistic Regression|逻辑回归

----------------------------------------------------------------------------------------------- 这是我在我的网站中截取的文章&#xff0c;有更多的文章欢迎来访问我自己的博客网站rn.berlinlian.cn&#xff0c;这里还有很多有关计算机的知识&#xff0c;欢迎进行留言或…