<think>我们被要求使用pytest和requests实现接口功能自动化测试。根据引用资料,我们可以总结出以下关键步骤和思路:
1. 环境准备:安装Python,使用pip安装requests和pytest库。
2. 对requests进行二次封装,以便统一处理请求、响应和异常。
3. 使用Excel或其他数据文件来管理测试数据(数据驱动)。
4. 编写测试用例,使用pytest管理测试用例(通常以test_*.py或*_test.py命名)。
5. 使用断言进行结果验证。
6. 生成测试报告(如Allure报告)和日志记录。
具体实现步骤:
1. 封装requests请求
我们可以创建一个类或模块来封装常见的HTTP方法(GET, POST, PUT, DELETE等),这样可以在发送请求时统一添加头信息、处理异常、记录日志等。
2. 准备测试数据
通常将测试数据放在外部文件(如Excel、JSON、YAML)中,然后通过数据驱动的方式读取。pytest支持通过`@pytest.mark.parametrize`装饰器实现参数化。
3. 编写测试用例
测试用例函数应该以`test_`开头,这样pytest可以自动发现。在测试用例中,调用封装的请求方法发送请求,然后对响应进行断言。
4. 断言
使用Python的`assert`语句或pytest提供的断言方法进行结果验证。
5. 生成报告
可以使用Allure框架生成漂亮的测试报告。需要安装Allure并配置pytest使用Allure。
6. 日志记录
使用Python的logging模块记录测试过程中的信息,便于调试和查看。
下面是一个简单的示例:
**步骤1:封装requests**
创建一个文件`api_client.py`,内容如下:
```python
import requests
import logging
class APIClient:
def __init__(self, base_url):
self.base_url = base_url
self.session = requests.Session()
def get(self, endpoint, params=None, headers=None):
url = self.base_url + endpoint
try:
response = self.session.get(url, params=params, headers=headers)
response.raise_for_status() # 如果响应状态不是200,将抛出异常
return response
except requests.exceptions.RequestException as e:
logging.error(f"GET请求失败: {e}")
raise
def post(self, endpoint, data=None, json=None, headers=None):
url = self.base_url + endpoint
try:
response = self.session.post(url, data=data, json=json, headers=headers)
response.raise_for_status()
return response
except requests.exceptions.RequestException as e:
logging.error(f"POST请求失败: {e}")
raise
```
**步骤2:编写测试用例**
创建一个测试文件`test_example.py`,内容如下:
```python
import pytest
from api_client import APIClient
# 假设我们的基础URL
BASE_URL = "https://jsonplaceholder.typicode.com"
# 创建客户端实例
@pytest.fixture(scope="module")
def api_client():
return APIClient(BASE_URL)
# 测试用例:获取帖子列表
def test_get_posts(api_client):
response = api_client.get("/posts")
assert response.status_code == 200
data = response.json()
assert isinstance(data, list)
assert len(data) > 0
# 测试用例:创建新帖子
@pytest.mark.parametrize("userId, title, body", [
(1, "test title", "test body")
])
def test_create_post(api_client, userId, title, body):
json_data = {
"userId": userId,
"title": title,
"body": body
}
response = api_client.post("/posts", json=json_data)
assert response.status_code == 201
created_post = response.json()
assert created_post['userId'] == userId
assert created_post['title'] == title
assert created_post['body'] == body
```
**步骤3:运行测试并生成报告**
首先安装Allure-pytest:
```bash
pip install allure-pytest
```
然后运行测试并生成Allure报告:
```bash
pytest --alluredir=./allure-results
```
最后,使用Allure命令生成HTML报告:
```bash
allure serve ./allure-results
```
这样,我们就完成了一个简单的接口自动化测试框架的搭建。
**注意事项**:
- 测试数据管理:更复杂的数据驱动可以通过读取外部文件(如Excel)来实现,然后使用`pytest.mark.parametrize`参数化。
- 配置管理:可以将基础URL等配置信息放在配置文件中(如config.ini或config.json),然后在代码中读取。
- 日志配置:可以配置日志级别、格式和输出位置。
以上是一个基本的实现思路,根据实际项目需求可以进一步扩展和优化。
§§相关问题§§
1. 如何将测试数据存储在Excel文件中并进行数据驱动测试?
2. 在接口自动化测试中如何处理登录状态的保持(如cookies或token)?
3. 如何使用Allure报告添加详细的测试步骤描述?
4. 如何对接口返回的数据进行复杂的验证(如使用JSONPath)?
5. 在接口测试中如何模拟第三方接口的响应?</think>### 如何使用 pytest 和 requests 实现接口功能自动化测试
接口自动化测试的实现主要包含以下核心步骤,结合 pytest 和 requests 的完整流程如下:
---
#### 1. **环境准备**
```bash
pip install requests pytest pytest-html allure-pytest
```
---
#### 2. **目录结构设计**
```markdown
project/
├── api/ # 接口封装层
│ └── user_api.py # 用户相关接口封装
├── testcases/ # 测试用例层
│ └── test_user.py # 用户接口测试用例
├── data/ # 测试数据层
│ └── user_data.yaml # 数据驱动文件
├── utils/ # 工具层
│ ├── request_util.py # requests二次封装
│ └── assert_util.py # 断言工具
├── conftest.py # pytest全局配置
└── reports/ # 测试报告目录
```
---
#### 3. **核心实现步骤**
##### (1) 封装 requests 工具 (utils/request_util.py)
```python
import requests
import logging
class RequestUtil:
def __init__(self):
self.session = requests.Session()
self.base_url = "https://api.example.com" # 基础URL
def send_request(self, method, endpoint, **kwargs):
url = f"{self.base_url}{endpoint}"
try:
response = self.session.request(method, url, **kwargs)
response.raise_for_status()
logging.info(f"请求成功: {method} {url}")
return response.json()
except requests.exceptions.RequestException as e:
logging.error(f"请求失败: {e}")
raise
```
##### (2) 封装断言工具 (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_data = response.json()
assert key in json_data, f"响应中缺少关键字段: {key}"
def assert_json_value(response, key, expected_value):
json_data = response.json()
assert json_data[key] == expected_value, \
f"字段{key}值不匹配, 预期:{expected_value}, 实际:{json_data[key]}"
```
##### (3) 接口业务封装 (api/user_api.py)
```python
from utils.request_util import RequestUtil
class UserAPI:
def __init__(self):
self.request = RequestUtil()
def login(self, username, password):
"""登录接口"""
endpoint = "/login"
payload = {"username": username, "password": password}
return self.request.send_request("POST", endpoint, json=payload)
def get_user_info(self, user_id):
"""获取用户信息"""
endpoint = f"/users/{user_id}"
return self.request.send_request("GET", endpoint)
```
##### (4) 编写测试用例 (testcases/test_user.py)
```python
import pytest
from api.user_api import UserAPI
from utils.assert_util import *
@pytest.fixture(scope="module")
def user_api():
return UserAPI()
# 参数化测试数据
@pytest.mark.parametrize("username, password", [
("admin", "admin123"),
("testuser", "test456")
])
def test_login_success(user_api, username, password):
"""测试登录成功场景"""
response = user_api.login(username, password)
assert_status_code(response, 200)
assert_json_key(response, "token")
def test_get_user_info(user_api):
"""测试获取用户信息"""
response = user_api.get_user_info(1)
assert_json_value(response, "username", "admin")
assert_json_value(response, "email", "admin@example.com")
```
##### (5) 全局配置 (conftest.py)
```python
import pytest
import logging
@pytest.fixture(autouse=True)
def setup_logging():
logging.basicConfig(level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
# 失败用例重试机制
def pytest_addoption(parser):
parser.addoption("--retries", action="store", default=1, type=int)
@pytest.hookimpl(tryfirst=True)
def pytest_runtest_makereport(item, call):
if "retry" in item.keywords:
if call.excinfo is not None:
item.add_marker(pytest.mark.xfail(reason="Flaky test"))
```
---
#### 4. **执行测试与报告生成**
```bash
# 执行测试并生成HTML报告
pytest --html=reports/report.html
# 使用Allure生成高级报告
pytest --alluredir=reports/allure-results
allure serve reports/allure-results
```
---
#### 5. **关键实践技巧
1. **数据驱动**
使用 YAML/JSON 管理测试数据:
```yaml
# data/user_data.yaml
login_cases:
- case: 正确密码
username: "admin"
password: "admin123"
expected_code: 200
- case: 错误密码
username: "testuser"
password: "wrong"
expected_code: 401
```
2. **关联接口处理**
通过 session 保持登录状态:
```python
@pytest.fixture
def auth_session(user_api):
user_api.login("admin", "admin123")
return user_api.request.session # 返回已认证的session
```
3. **自动化断言扩展**
使用 JSONSchema 验证响应结构:
```python
from jsonschema import validate
schema = {
"type": "object",
"properties": {
"id": {"type": "number"},
"username": {"type": "string"},
"email": {"type": "string", "format": "email"}
},
"required": ["id", "username"]
}
def test_user_schema(user_api):
response = user_api.get_user_info(1)
validate(instance=response.json(), schema=schema)
```
---
### 典型问题解决方案
| 问题类型 | 解决方案 |
|------------------|--------------------------------------------------------------------------|
| 接口依赖 | 使用 `pytest.fixture` 管理前置条件 |
| 测试数据管理 | YAML/Excel + `@pytest.mark.parametrize` 实现数据驱动 |
| 异步接口验证 | 添加轮询机制(如使用 `tenacity` 库) |
| 敏感数据处理 | 使用 `python-dotenv` 管理环境变量 |
| 多环境切换 | 通过命令行参数切换 base_url |
| 文件上传/下载 | 使用 requests 的 `files` 参数和流式下载 |
通过以上实现,可构建可维护的接口自动化测试框架,平均提升测试效率 60% 以上[^5]。