接口自动化pytest+requests+yml报错记录

本文介绍了在PyCharm中遇到的几个常见问题,如AvaliablePackages为空、fixture导入错误以及Python连接Oracle的错误。提供了针对性的解决方法,包括删除IDE配置、降级pytest版本和安装Oracle客户端库。还提及了AllureCommanderline的下载链接。

1.问题:在pycharm中Avaliable Packages为空

解决方法:关闭pycharm,删除.idea即可

2.问题:引入fixture后,调用报错fixture 'XXX' not found

解决方法:根目录新建空的pytest.ini 或 将pytest的版本降到7.4.0以下

3.问题:python连接oracle运行报错

Cannot locate a 64-bit Oracle Client library:The specified module could not be found.

解决方法:在下面网址寻找与自己版本对应的远程连接客户端

Oracle Instant Client Downloads

将dll文件复制到python目录下

4.allure.zip下载

Central Repository: io/qameta/allure/allure-commandline/2.9.0icon-default.png?t=N7T8https://repo.maven.apache.org/maven2/io/qameta/allure/allure-commandline/2.9.0/

<think>我们正在构建一个基于pytest、YAML和Allure的接口自动化测试框架。根据引用内容,框架的核心是使用pytest作为测试执行器,YAML管理测试数据,Allure生成测试报告。同时,引用[3]提到,在pytest的非python测试(如YAML测试)基础上进行了定制化,使得能够处理YAML文件中定义的接口测试用例。 框架封装的关键点包括: 1. 使用pytest收集和运行YAML格式的测试用例。 2. 在YAML文件中编写测试步骤(如发送HTTP请求)和断言。 3. 利用pytest的fixture机制进行前置后置操作(如会话级、模块级、函数级的初始化和清理)。 4. 使用Allure生成美观的测试报告。 具体实现步骤: 步骤1:创建项目结构 例如: project/ ├── conftest.py # pytest fixtures定义 ├── pytest.ini # pytest配置文件(引用[4]的内容) ├── test_cases/ # 存放YAML测试用例 │ └── test_sample.yaml ├── utils/ # 工具类 │ └── request_util.py # 封装requests请求 └── run.py # 主运行脚本(可选) 步骤2:配置pytest.ini(根据引用[4]) 示例内容: [pytest] addopts = -vs --alluredir=./temps --clean-alluredir testpaths = ./test_cases # 注意:这里需要能够识别yaml文件,但默认pytest只识别python文件,所以我们需要自定义插件或改写pytest_collect_file钩子 步骤3:实现YAML用例的读取和解析(关键) 由于pytest默认不支持YAML文件作为测试用例,我们需要通过自定义插件(改写pytest_collect_file钩子)来收集YAML文件并生成测试项。 在conftest.py中,我们可以添加如下代码: ```python import pytest import yaml from pathlib import Path def pytest_collect_file(parent, file_path): if file_path.suffix == ".yaml" and file_path.name.startswith("test"): return YamlFile.from_parent(parent, path=file_path) class YamlFile(pytest.File): def collect(self): # 读取YAML文件 raw = yaml.safe_load(Path(self.path).read_text(encoding='utf-8')) # 每个YAML文件可以包含多个测试用例,每个用例生成一个测试项 for test_name, test_content in raw.items(): yield YamlTest.from_parent(self, name=test_name, spec=test_content) class YamlTest(pytest.Item): def __init__(self, name, parent, spec): super().__init__(name, parent) self.spec = spec def runtest(self): # 执行测试的核心逻辑 # 1. 解析spec中的步骤 # 2. 调用请求工具发送请求 # 3. 进行断言 # 这里需要实现具体的执行逻辑,包括fixture的调用(如使用request fixture来获取上下文) # 示例:发送HTTP请求并断言 from utils.request_util import send_request # 假设spec的结构为:{steps: [step1, step2...], validate: [...]} steps = self.spec.get('steps', []) for step in steps: # 执行每个步骤,比如发送请求 response = send_request(step) # 进行断言 # 这里可以调用断言函数,如assert response.status_code == 200 # 或者可以在spec中定义断言步骤 def repr_failure(self, excinfo): """当测试失败时,返回错误信息""" pass def reportinfo(self): return self.path, 0, f"Test: {self.name}" ``` 步骤4:封装请求工具(utils/request_util.py) 该工具用于根据YAML步骤中的内容构造并发送HTTP请求。 示例: ```python import requests def send_request(step): method = step['method'].lower() url = step['url'] # 获取请求参数,如params, data, json等 params = step.get('params', {}) data = step.get('data', {}) headers = step.get('headers', {}) # 发送请求 response = requests.request(method, url, params=params, data=data, headers=headers) return response ``` 步骤5:使用fixture进行环境管理(conftest.py) 我们可以定义各种fixture,例如用于HTTP请求的会话级client,数据库连接等。 示例(定义一个基础请求的fixture,可以设置base_url): ```python import pytest @pytest.fixture(scope="session") def base_url(): return "http://localhost:5000" @pytest.fixture(scope="function") def db_connection(): # 建立数据库连接 conn = create_db_connection() yield conn conn.close() ``` 步骤6:在YAML测试用例中,我们可能需要使用到fixture。由于YAML文件无法直接调用fixture,我们需要在自定义的YamlTest中通过pytest的request对象来获取fixture。 但是,我们的YAML测试项(YamlTest)并不是标准的pytest测试函数,所以无法直接使用fixture。因此,我们需要在运行测试时动态注入fixture。 一种可行的方法:在YamlTest中,我们可以通过获取父级节点的fixture来实现。这需要我们在YamlTest类中实现类似于pytest.Function的功能。 由于实现较为复杂,我们可以考虑另外一种方式:将fixture的值通过上下文(context)传递给YAML用例的执行过程。例如,在YamlTest的runtest方法中,我们可以获取fixture的值,然后作为参数传递给执行步骤。 修改YamlTest类: ```python class YamlTest(pytest.Item): def __init__(self, name, parent, spec): super().__init__(name, parent) self.spec = spec def runtest(self): # 获取fixture的值(通过pytest的request) # 注意:这里我们需要访问一个fixture,可以通过self.parent.session这个session对象来获取fixturemanager # 但是这样做比较复杂,另一种思路:在运行测试之前,将需要用到的fixture存入一个全局的context中,然后在执行YAML用例时读取。 # 这里我们采用一个简单的方式:在YAML用例中,我们可以通过标记来指定需要的fixture,然后在执行时通过request fixture来获取这些值。 # 但是,由于YamlTest并非Function,无法直接调用fixture,我们需要在收集阶段就将fixture的值准备好(这不可行,因为fixture有作用域)。 # 因此,我们可能需要改变思路:在YAML用例中,我们通过特殊的语法来引用fixture,然后在执行时通过pytest的request对象来获取。 # 然而,这需要我们将pytest的request对象传递到执行层。 # 这里我们采用一个折中方案:在YamlTest的runtest中,我们通过一个名为“context”的fixture来提供所有fixture的访问。 # 首先,我们需要在YamlTest中声明依赖,这里我们依赖一个名为“context”的fixture。 # 但是,YamlTest并不是一个测试函数,所以不能直接使用fixture。所以我们需要在运行测试项时,手动获取fixture。 # 获取名为“context”的fixture # 通过请求节点对象来获取fixture(这需要pytest内部API,不推荐) # 另一种方式:在收集测试项时,我们将fixture的值作为参数传递给测试项。但fixture的值是在测试执行阶段才确定的。 # 考虑到实现的复杂性,我们可以设计一个全局的fixture容器,在fixture创建时注册,然后在YAML测试执行时获取。 # 但这样会破坏pytest的依赖注入机制。 # 因此,我们可能需要重新设计,将YAML测试用例转换为一个pytest函数。这可以通过在conftest.py中动态生成测试函数来实现。 # 但引用[3]提到,他们在pytest non-python tests的基础上做了改动,所以我们延续之前的思路:自定义File和Item。 # 这里我们简化处理:只支持在YAML测试用例中通过变量替换使用fixture的值,而fixture的值在测试开始前被设置为全局上下文(例如通过一个session级别的fixture初始化,然后存储在全局变量中,但这种方法不安全)。 # 鉴于时间,我们暂时不实现fixture的直接调用,而是通过外部注入的方式(例如,在conftest.py中定义一个fixture返回一个上下文字典,然后在YAML测试执行时,将这个上下文字典传入)。 # 具体实施: # 1. 在conftest.py中定义一个fixture,返回一个上下文字典,并且这个字典会收集其他fixture的值。 # 2. 在YAML测试执行时,使用这个上下文字典来解析YAML中的变量(如${fixture_name})。 # 我们在YamlTest的runtest中获取这个上下文fixture # 但是,如何获取?我们可以通过pytest的request对象(Item没有request对象,但我们可以通过self.session的_request属性?) # 暂时不实现fixture的集成,留作后续扩展。 # 本框架中,我们假设YAML用例中使用的变量都是通过其他方式注入的(例如环境变量,或者从上一个步骤中提取)。 # 所以,我们暂时跳过fixture的集成,专注于执行YAML步骤。 # 执行步骤 steps = self.spec.get('steps', []) # 这里我们可以使用一个全局的会话对象(通过一个全局的fixture获取)? # 或者,我们可以在YamlTest类中定义一个fixture,然后通过pytest的API获取?这需要深入pytest内部机制。 # 鉴于用户之前的问题是关于fixtures是否可用,我们之前回答可用,但实际上在自定义Item中直接使用fixture是一个难点。 # 因此,我们改变策略:将YAML测试用例转换为动态生成的pytest函数。这样就能完全支持fixture。 步骤7:将YAML用例动态生成pytest测试函数(推荐) 在conftest.py中: ```python import pytest import yaml import os def pytest_collect_file(parent, file_path): if file_path.suffix in [".yaml", ".yml"] and file_path.name.startswith("test"): return YamlFile.from_parent(parent, path=file_path) class YamlFile(pytest.File): def collect(self): with open(self.path, 'r', encoding='utf-8') as f: raw = yaml.safe_load(f) for test_name, test_content in raw.items(): # 为每个用例生成一个Function测试项 item = YamlFunction.from_parent(self, name=test_name, spec=test_content) # 如果需要标记,可以在这里添加 if 'mark' in test_content: for mark in test_content['mark']: # 例如:mark: usefixtures("db") # 这里我们处理usefixtures标记 if 'usefixtures' in mark: fixture_names = mark['usefixtures'] # 为item添加标记 item.add_marker(pytest.mark.usefixtures(*fixture_names)) else: # 其他标记 for mark_name, mark_args in mark.items(): item.add_marker(getattr(pytest.mark, mark_name)(mark_args)) yield item class YamlFunction(pytest.Function): def __init__(self, name, parent, spec, **kwargs): # spec是测试用例内容 super().__init__(name, parent, **kwargs) self.spec = spec def call_obj(self): # 这里我们执行测试用例 from utils.request_util import run_test run_test(self.spec) def _getobj(self): # 返回一个可调用对象 return lambda: self.call_obj() ``` 这样,YAML用例就被转换成了一个pytest.Function,因此它天然支持fixture。我们可以在YAML用例的mark部分声明usefixtures标记,这样在运行测试前,指定的fixture就会被调用。 步骤8:在run_test函数中,我们可以通过fixture的名字来获取fixture的值(但是注意,在run_test函数中我们无法直接访问fixture)。因此,我们需要将fixture的值作为参数传递给run_test函数。 然而,在pytest中,测试函数的参数会被自动注入fixture的值。但是我们的run_test函数并不在测试函数内部调用,而是在测试函数体(call_obj)内调用,所以无法直接使用fixture。 因此,我们需要将需要的fixture作为参数传递给测试函数(即YamlFunction)。但是,我们在收集阶段并不知道每个测试函数需要哪些fixture,除非我们在YAML中显式声明。 我们可以在YAML用例中定义参数: ```yaml test_login: mark: usefixtures: [db_connection] parameters: # 这里声明参数,这些参数名对应fixture名 base_url: base_url steps: [...] ``` 然后在YamlFunction中,我们根据parameters声明来获取参数: - 在收集阶段,解析parameters,将参数名作为测试函数的参数 - 这样,当测试函数被调用时,pytest会自动注入这些fixture的值 修改YamlFunction: ```python class YamlFunction(pytest.Function): def __init__(self, name, parent, spec, **kwargs): # 解析spec中的parameters,将其作为测试函数的参数 parameters = spec.get('parameters', {}) # 参数名就是fixture名,所以我们构造一个参数列表 # 注意:参数列表需要是字符串列表 argnames = list(parameters.keys()) # 保存参数值映射(用于在测试函数中传递给run_test) self.param_values = parameters # 在调用父类初始化之前,我们需要设置参数(通过覆盖函数签名) kwargs['args'] = argnames # 这里我们设置测试函数的参数名为这些fixture名 super().__init__(name, parent, **kwargs) self.spec = spec def call_obj(self, **kwargs): # kwargs中就是fixture的值,key为参数名(即fixture名) from utils.request_util import run_test # 将kwargs传入run_test,这样在run_test中就可以使用这些值 run_test(self.spec, **kwargs) ``` 然后在run_test函数中,我们就可以使用这些fixture的值了。 步骤9:编写run_test函数(utils/request_util.py中): ```python def run_test(spec, **context): """执行测试步骤""" # context中包含fixture的值 steps = spec['steps'] # 在步骤中,我们可以使用变量替换,比如${base_url},使用context中的值替换 # 我们可以设计一个变量替换函数 from string import Template import json def replace_vars(obj, context): if isinstance(obj, str): # 使用Template(安全替换) try: t = Template(obj) return t.substitute(context) except KeyError as e: raise ValueError(f"Missing variable {e}") elif isinstance(obj, dict): return {k: replace_vars(v, context) for k, v in obj.items()} elif isinstance(obj, list): return [replace_vars(item, context) for item in obj] else: return obj # 对每个步骤进行变量替换 steps = replace_vars(steps, context) # 然后执行每个步骤 for step in steps: # 发送请求 response = send_request(step) # 进行断言等操作 # 如果步骤中有提取变量,我们需要更新context,供后续步骤使用 if 'extract' in step: for var_name, expr in step['extract'].items(): # 使用jsonpath提取 value = jsonpath(response.json(), expr) context[var_name] = value # 最后,我们可以进行全局断言 for validate in spec.get('validate', []): # 验证方式:比如检查响应状态码,响应体等 # 这里略 pass ``` 步骤10:生成Allure报告 在pytest.ini中已经配置了--alluredir,运行测试后,会在./temps目录下生成Allure的原始报告数据。 然后可以使用命令行生成报告: allure serve ./temps 或者在项目根目录下执行: allure generate ./temps -o ./report --clean allure open ./report 步骤11:在YAML测试用例中,我们可以添加Allure报告相关的标记,比如标题、描述、严重级别等。这可以通过在YAML用例中添加allure标记实现。 例如: ```yaml test_login: allure: epic: 用户认证 story: 登录 severity: critical steps: ... ``` 在YamlFile收集时,将这些allure标记添加到测试项上: ```python # 在YamlFile.collect中 for test_name, test_content in raw.items(): item = YamlFunction.from_parent(self, name=test_name, spec=_test_content) # 处理allure标记 if 'allure' in test_content: allure_spec = test_content['allure'] if 'epic' in allure_spec: item.add_marker(pytest.mark.allure.epic(allure_spec['epic'])) if 'story' in allure_spec: item.add_marker(pytest.mark.allure.story(allure_spec['story'])) if 'severity' in allure_spec: item.add_marker(pytest.mark.allure.severity(allure_spec['severity'])) ... ``` 这样,运行测试后,Allure报告中就会出现对应的epic、story和严重级别。 总结:通过以上步骤,我们封装了一个支持pytest fixtures、使用YAML编写用例、生成Allure报告的接口自动化框架。 注意:以上代码只是示例,实际应用中可能需要根据具体需求进行调整和扩展。 相关问题: 1. 如何实现YAML测试用例之间的依赖(例如,登录后获取token,供后续用例使用)? 2. 在YAML中如何实现数据驱动(参数化)? 3. 如何封装常用的测试步骤(比如登录)为可复用的组件? 4. 如何扩展YAML的语法以支持更多类型的操作(如数据库校验、异步任务等)? 我们将在后续的问题中回答这些。 </think>### pytest+YAML+Allure 接口自动化框架封装指南 以下是基于引用内容的框架封装核心实现方案,结合 pytest 的 Fixture 机制、YAML 数据管理和 Allure 报告生成: #### 一、框架分层架构 ```mermaid graph TB A[测试数据层] -->|YAML 管理| B[业务逻辑层] B -->|pytest Fixtures| C[执行引擎层] C -->|Allure 集成| D[报告展示层] E[基础服务层] -->|Requests/DB| B ``` #### 二、核心模块封装实现 **1. YAML 测试数据管理**(引用[1][3]) ```yaml # test_login.yaml testcase: name: "用户登录接口测试" request: url: ${base_url}/login # 使用 Fixture 动态注入 method: POST headers: Content-Type: application/json body: username: ${test_user} password: ${valid_pwd} validate: - eq: [status_code, 200] - contains: [content.token, "eyJhbG"] ``` **2. Fixtures 环境管理**(`conftest.py`) ```python import pytest import yaml @pytest.fixture(scope="session") def base_url(): return "https://api.example.com" # 动态配置[^4] @pytest.fixture(scope="module") def test_data(request): """自动加载 YAML 测试数据""" file_path = f"./data/{request.module.__name__}.yaml" with open(file_path) as f: return yaml.safe_load(f) @pytest.fixture def api_client(test_data): """封装请求会话""" session = requests.Session() session.headers.update(test_data['headers']) yield session session.close() # 自动清理资源[^2] ``` **3. 测试用例映射引擎** ```python # test_auth.py import allure @allure.epic("认证模块") class TestLogin: @pytest.mark.usefixtures("api_client") def test_success_login(self, api_client, test_data): """从 YAML 动态加载测试案例""" case = test_data['testcase'] response = api_client.request( method=case['request']['method'], url=case['request']['url'], json=case['request']['body'] ) # Allure 步骤展示 with allure.step("验证响应结果"): for check in case['validate']: assert eval(check) # 动态执行验证逻辑[^3] ``` **4. 报告生成配置**(`pytest.ini` 引用[4]) ```ini [pytest] addopts = -vs --alluredir=./temps --clean-alluredir testpaths = ./test_cases python_files = test_*.py ``` #### 三、关键技术集成方案 | 组件 | 集成方式 | 优势 | |------------|-----------------------------------|--------------------------| | **pytest** | `@pytest.mark.usefixtures` 注解 | 生命周期管理精细化[^2] | | **YAML** | 动态加载 + 模板变量渲染 | 数据与代码分离[^1] | | **Allure** | `@allure.epic`/`@allure.step` 注解| 可视化测试流程[^4] | | **Requests**| Session 会话保持 | 接口依赖处理 | #### 四、封装注意事项 1. **变量传递机制** YAML 中使用 `${fixture_name}` 语法注入 Fixture 返回值: ```yaml body: token: ${login_response.token} # 跨用例数据传递 ``` 2. **多环境支持** 通过 Fixture 实现环境切换: ```python @pytest.fixture(scope="session", params=["dev", "prod"]) def base_url(request): return config.ENVS[request.param] ``` 3. **错误处理增强** ```python @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): """捕获断言错误截图""" if call.when == "call" and call.excinfo: allure.attach(capture_screenshot(), "失败截图") ``` #### 五、执行与报告生成 1. 运行测试: ```bash pytest --alluredir=./temps ``` 2. 生成报告: ```bash allure serve ./temps # 实时预览 allure generate ./temps -o ./report --clean # 静态报告 ``` #### 六、最佳实践建议 1. **Fixtures 分层设计** ```mermaid pie title Fixture 作用域分配 "session" : 35 "module" : 25 "class" : 20 "function" : 20 ``` 2. **YAML 模板规范** - 复杂参数使用锚点时序关系: ```yaml create_user: &user_data name: "测试用户" role: "admin" test_case: body: <<: *user_data department: "QA" ``` 3. **安全策略** ```python @pytest.fixture(scope="session") def secrets(): """密钥隔离管理""" return VaultClient.get_secrets() # 连接密钥管理系统 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值