pytest接口自动化测试框架+项目实例

转载:https://blog.youkuaiyun.com/wshlp123456/article/details/89194095

python pytest (或许以后加上django)

一、基础框架:测试用例;测试数据;测试报告------------实现逻辑和数据分离,后期可以增加日志、公用配置、封装完善

1、  项目背景:http 接口、pycharm、pytest/unitest、python 

先实现一个接口执行,引入 requests,json库 (我习惯用pyinstaller先打包成exe)

执行成功。开始实现多个接口执行,并且引入pytest

2、整合所有接口,加入assert断言,main函数里添加生产html报告

cmd下 执行语句:pytest -v test_xxx.py --html=report\test_x.html,可以实现生成report目录下执行test_xxx.py的test_x.html形式报告

写在程序里即执行 :


 
  1. if __name__ == '__main__':
  2. pytest.main(['--html=../report/test_x.html', 'test_xxx.py'])

3、实现参数化

引入xlrd 库,用excel 管理测试用例,实现一个接口对应多个测试用例

同样,最后加上生成报告语句,即可。

 

<think>我们使用Python和pytest搭建一个接口自动化测试框架。这个框架应该包括以下基本组件:1.配置文件管理:用于管理环境配置(如测试环境、生产环境的URL等)2.请求封装:封装HTTP请求方法(GET,POST,PUT,DELETE等)3.测试用例管理:使用pytest编写和管理测试用例4.数据驱动:支持使用数据文件(如JSON,YAML,Excel)来驱动测试5.断言:丰富的断言机制6.测试报告:生成测试报告(可以使用pytest-html或其他插件)7.日志记录:记录测试过程中的日志下面我们逐步构建这个框架。步骤1:项目结构创建一个项目目录,例如:api_auto_test,然后创建以下子目录和文件:-config/:存放配置文件__init__.pysettings.py#配置文件-data/:存放测试数据文件-logs/:存放日志文件-reports/:存放测试报告-testcases/:存放测试用例__init__.pytest_sample.py#示例测试用例-utils/:存放工具类__init__.pyrequest_util.py#请求封装assert_util.py#断言工具log_util.py#日志工具-conftest.py:pytest的全局配置文件-pytest.ini:pytest的配置文件-requirements.txt:项目依赖步骤2:配置文件(config/settings.py)定义不同环境的配置,例如:步骤3:日志工具(utils/log_util.py)配置日志记录。步骤4:请求封装(utils/request_util.py)使用requests库封装HTTP请求。步骤5:断言工具(utils/assert_util.py)封装一些常用的断言方法,比如检查状态码、响应体包含某些内容等。步骤6:测试用例(testcases/test_sample.py)编写测试用例,使用pytest的格式。步骤7:数据驱动我们可以使用pytest的parametrize来实现数据驱动,数据可以来自文件。步骤8:测试报告运行pytest时可以生成HTML报告。步骤9:conftest.py可以在这里定义一些fixture,例如初始化日志、设置全局变量等。下面我们逐一实现这些部分。注意:在开始之前,确保已经安装必要的库:requests,pytest,pytest-html,pyyaml(如果需要处理yaml数据)等。由于篇幅限制,我将给出关键部分的代码示例,并做简要解释。1.配置文件(config/settings.py)```python#config/settings.pyclassConfig:#测试环境配置TEST_ENV={"base_url":"http://httpbin.org"}#生产环境配置PROD_ENV={"base_url":"https://prod.example.com"}#当前使用的环境CURRENT_ENV=TEST_ENVconfig=Config()```2.日志工具(utils/log_util.py)```python#utils/log_util.pyimportloggingimportosfromdatetimeimportdatetimedefsetup_logger():#创建日志目录log_dir="logs"ifnotos.path.exists(log_dir):os.makedirs(log_dir)#日志文件名格式:年-月-日.loglog_file=datetime.now().strftime("%Y-%m-%d")+".log"log_path=os.path.join(log_dir,log_file)#配置日志logging.basicConfig(level=logging.INFO,format='%(asctime)s-%(name)s-%(levelname)s-%(message)s',datefmt='%Y-%m-%d%H:%M:%S',handlers=[logging.FileHandler(log_path),logging.StreamHandler()])logger=logging.getLogger("api_test")returnloggerlogger=setup_logger()```3.请求封装(utils/request_util.py)```python#utils/request_util.pyimportrequestsfromutils.log_utilimportloggerfromconfig.settingsimportconfigclassRequestUtil:def__init__(self):self.session=requests.Session()self.base_url=config.CURRENT_ENV["base_url"]defrequest(self,method,url,**kwargs):#拼接完整的URLfull_url=self.base_url+urllogger.info(f"请求方法:{method},请求URL:{full_url}")#记录请求数据if"params"inkwargs:logger.info(f"请求参数:{kwargs['params']}")if"json"inkwargs:logger.info(f"请求体:{kwargs['json']}")if"data"inkwargs:logger.info(f"请求表单数据:{kwargs['data']}")#发送请求response=self.session.request(method,full_url,**kwargs)#记录响应logger.info(f"响应状态码:{response.status_code}")logger.info(f"响应内容:{response.text}")returnresponsedefget(self,url,params=None,**kwargs):returnself.request("GET",url,params=params,**kwargs)defpost(self,url,data=None,json=None,**kwargs):returnself.request("POST",url,data=data,json=json,**kwargs)defput(self,url,data=None,json=None,**kwargs):returnself.request("PUT",url,data=data,json=json,**kwargs)defdelete(self,url,**kwargs):returnself.request("DELETE",url,**kwargs)```4.断言工具(utils/assert_util.py)```python#utils/assert_util.pyfromutils.log_utilimportloggerdefassert_status_code(response,expected_code):assertresponse.status_code==expected_code,\f"期望状态码{expected_code},实际状态码{response.status_code}"logger.info(f"状态码断言成功:期望{expected_code},实际{response.status_code}")defassert_json_value(response,key,expected_value):json_data=response.json()actual_value=json_data.get(key)assertactual_value==expected_value,\f"期望{key}的值为{expected_value},实际为{actual_value}"logger.info(f"JSON值断言成功:键{key},期望值{expected_value},实际值{actual_value}")#可以继续扩展其他断言方法```5.测试用例示例(testcases/test_sample.py)```python#testcases/test_sample.pyimportpytestfromutils.request_utilimportRequestUtilfromutils.assert_utilimportassert_status_code,assert_json_value#创建请求工具实例request_util=RequestUtil()#测试用例deftest_get_ip():url="/ip"response=request_util.get(url)assert_status_code(response,200)#检查返回的origin字段是否存在(httpbin.org返回IP地址)assert_json_value(response,"origin","127.0.0.1")#注意:这里期望值应该是你实际访问的IP,但测试时可能不固定,所以这个断言可能会失败,仅作示例#使用数据驱动@pytest.mark.parametrize("param,expected",[({"name":"John"},"John"),({"name":"Alice"},"Alice")])deftest_get_with_param(param,expected):url="/get"response=request_util.get(url,params=param)assert_status_code(response,200)#检查URL参数是否被正确返回assert_json_value(response,"args",param)#检查返回的args中name的值assertresponse.json()["args"]["name"]==expected```6.conftest.py```python#conftest.pyimportpytestfromutils.log_utilimportlogger@pytest.fixture(scope="session",autouse=True)defsetup_session():logger.info("测试开始")yieldlogger.info("测试结束")```7.pytest.ini```[pytest]addopts=-v-s--html=reports/report.html--self-contained-htmltestpaths=testcases```8.requirements.txt```requestspytestpytest-html```运行测试:在项目根目录下执行命令:pytest注意:上面的测试用例test_get_ip中,期望的IP地址是"127.0.0.1",但实际上httpbin.org返回的是你的公网IP,所以这个断言会失败。这里仅作为示例,实际测试中需要根据情况调整。以上就是一个基本的接口自动化测试框架的搭建。你可以根据实际需求扩展,比如添加数据库操作、邮件发送测试报告等功能。§§相关问题§§:1.如何在pytest中实现数据驱动,支持从YAML文件读取测试数据?2.如何将测试报告通过邮件发送?3.如何在测试框架中实现接口依赖(例如登录后获取token,其他接口使用该token)?4.如何实现测试用例的失败重试?</think>### Python + Pytest 接口自动化框架搭建 下面是一个完整的接口自动化框架搭建方案,包含目录结构、核心模块实现和示例代码: #### 目录结构 ``` api_auto_framework/ ├── config/ # 配置文件 │ ├── __init__.py │ ├── settings.py # 基础配置 │ └── test_env.yaml # 环境配置 ├── testcases/ # 测试用例 │ ├── __init__.py │ ├── test_user_api.py # 示例测试文件 ├── utils/ # 工具类 │ ├── __init__.py │ ├── request_util.py # 请求封装 │ ├── assert_util.py # 断言工具 │ ├── data_util.py # 数据工具 │ └── log_util.py # 日志工具 ├── conftest.py # Pytest全局配置 ├── pytest.ini # Pytest配置文件 └── requirements.txt # 依赖文件 ``` #### 核心模块实现 1. **配置管理 (config/settings.py)** ```python import os import yaml BASE_DIR = os.path.dirname(os.path.abspath(__file__)) def load_env_config(env="test"): """加载环境配置""" with open(os.path.join(BASE_DIR, "test_env.yaml"), encoding="utf-8") as f: all_envs = yaml.safe_load(f) return all_envs.get(env, {}) ``` 2. **请求封装 (utils/request_util.py)** ```python import requests from utils.log_util import logger class RequestUtil: def __init__(self, base_url=None): self.session = requests.Session() self.base_url = base_url or "" def request(self, method, endpoint, **kwargs): """发送HTTP请求""" url = f"{self.base_url}{endpoint}" try: response = self.session.request( method=method.upper(), url=url, **kwargs ) logger.info(f"请求: {method} {url} | 参数: {kwargs.get('params')} | 响应状态: {response.status_code}") return response except Exception as e: logger.error(f"请求异常: {method} {url} | 错误: {str(e)}") raise def get(self, endpoint, params=None, **kwargs): return self.request("GET", endpoint, params=params, **kwargs) def post(self, endpoint, data=None, json=None, **kwargs): return self.request("POST", endpoint, data=data, json=json, **kwargs) # 可继续封装PUT、DELETE等方法 ``` 3. **断言工具 (utils/assert_util.py)** ```python def assert_status_code(response, expected_code): """验证状态码""" assert response.status_code == expected_code, \ f"状态码错误! 预期: {expected_code}, 实际: {response.status_code}" def assert_json_key(response, key): """验证JSON响应包含指定key""" json_data = response.json() assert key in json_data, f"响应中缺少key: {key}" def assert_json_value(response, key, expected_value): """验证JSON字段值""" json_data = response.json() actual_value = json_data.get(key) assert actual_value == expected_value, \ f"值不匹配! Key: {key}, 预期: {expected_value}, 实际: {actual_value}" ``` 4. **日志工具 (utils/log_util.py)** ```python import logging import os from datetime import datetime def setup_logger(): """配置日志记录器""" logger = logging.getLogger("api_test") logger.setLevel(logging.INFO) # 创建日志目录 log_dir = "logs" os.makedirs(log_dir, exist_ok=True) # 日志文件名格式 log_file = f"{datetime.now().strftime('%Y%m%d_%H%M%S')}.log" file_handler = logging.FileHandler(os.path.join(log_dir, log_file)) # 日志格式 formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s" ) file_handler.setFormatter(formatter) logger.addHandler(file_handler) return logger logger = setup_logger() ``` 5. **全局配置 (conftest.py)** ```python import pytest from utils.request_util import RequestUtil from config.settings import load_env_config @pytest.fixture(scope="session") def api_client(): """全局API客户端""" env_config = load_env_config("test") base_url = env_config.get("base_url", "") return RequestUtil(base_url) @pytest.fixture(scope="function") def setup_data(): """测试数据准备""" return {"username": "testuser", "password": "Test@123"} ``` 6. **Pytest配置 (pytest.ini)** ```ini [pytest] addopts = -v --html=reports/report.html --self-contained-html testpaths = testcases log_cli = true log_level = INFO ``` #### 示例测试用例 (testcases/test_user_api.py) ```python import pytest class TestUserAPI: """用户接口测试用例""" def test_user_login(self, api_client, setup_data): """测试登录接口""" endpoint = "/api/login" payload = { "username": setup_data["username"], "password": setup_data["password"] } response = api_client.post(endpoint, json=payload) # 断言 assert response.status_code == 200 assert "token" in response.json() assert response.json().get("user") == setup_data["username"] @pytest.mark.parametrize("user_id", [1, 2, 3]) def test_get_user_info(self, api_client, user_id): """测试获取用户信息 - 参数化""" endpoint = f"/api/users/{user_id}" response = api_client.get(endpoint) # 断言 assert response.status_code == 200 assert response.json().get("id") == user_id ``` #### 环境配置示例 (config/test_env.yaml) ```yaml test: base_url: "https://api.example.com" db_host: "localhost" db_user: "test" prod: base_url: "https://prod-api.example.com" db_host: "db-prod.example.com" ``` #### 依赖文件 (requirements.txt) ``` pytest==7.4.0 requests==2.31.0 PyYAML==6.0.1 pytest-html==4.1.1 allure-pytest==2.13.2 ``` ### 框架特点说明: 1. **分层设计**: - 配置层:分离环境和测试数据 - 工具层:封装请求、断言、日志等公共能力 - 用例层:纯测试逻辑实现 2. **核心能力**: - 多环境支持(YAML配置) - 请求自动日志记录 - 丰富的断言方法 - HTML测试报告生成 - 参数化测试支持 3. **执行方式**: ```bash # 安装依赖 pip install -r requirements.txt # 运行测试 pytest # 生成Allure报告(可选) pytest --alluredir=./allure-results allure serve ./allure-results ``` ### 框架扩展建议: 1. 添加数据库操作模块用于数据验证 2. 集成CI/CD流水线支持 3. 添加API文档自动生成功能 4. 实现Mock服务用于依赖解耦
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值