告别Postman!用Python+Requests打造高可用的接口自动化框架
·
为什么选择离开舒适区?
Postman无疑是API测试的绝佳入门工具,直观的GUI让它易于上手。但当面临成百上千个接口、复杂的测试场景、CI/CD集成需求时,GUI操作变得笨重、难以维护和协作。而代码化(Code-based)的自动化框架,才是通往高效、可靠和可扩展的必经之路。
本文将手把手带你用Python的Requests库,从一个简单的请求开始,逐步构建一个结构清晰、功能完备的高可用接口自动化框架。
第一部分:为什么是Python + Requests?
- 极致简洁:
Requests库让HTTP请求变得像普通函数调用一样简单,远比原生urllib优雅。 - 完全掌控:代码意味着无限的可能性。你可以轻松地集成数据驱动、生成动态参数、连接数据库、定制报告、接入CI/CD(如Jenkins)。
- 易于维护和版本控制:脚本文件(.py)可以用Git等工具进行版本管理,轻松追溯变更、协作开发。
- 强大的生态系统:
Pytest(测试框架)、Allure(精美报告)、JSONPath(JSON解析)等众多库能与Requests无缝集成。
第二部分:四步搭建你的专属框架
我们的目标是构建一个结构分明、易于扩展的框架。最终目录结构将如下所示:
api_framework/
├── common/ # 公共模块
│ ├── __init__.py
│ ├── request_client.py # 封装的请求客户端
│ └── logger.py # 日志模块
├── config/ # 配置管理
│ ├── __init__.py
│ └── config.py
├── test_data/ # 测试数据
│ ├── __init__.py
│ └── user_data.py
├── test_cases/ # 测试用例
│ ├── __init__.py
│ └── test_user_api.py
├── reports/ # 测试报告(自动生成)
└── run_tests.py # 测试入口文件
第一步:基础封装——打造你的“超级Requests客户端”
目标:封装 Requests ,自动处理 base_url 、日志、并返回统一的响应格式。
创建 common/request_client.py :
import requests
import json
from common.logger import get_logger
logger = get_logger(__name__)
class RequestClient:
def __init__(self, base_url=""):
self.base_url = base_url
# 创建一个会话对象,可以自动保持cookies
self.session = requests.Session()
# 设置公共请求头
self.session.headers.update({
"Content-Type": "application/json; charset=utf-8"
})
def _request(self, method, url, **kwargs):
"""核心请求方法"""
# 拼接完整的请求URL
full_url = self.base_url + url
logger.info(f"请求方法: {method}")
logger.info(f"请求URL: {full_url}")
if 'json' in kwargs:
logger.info(f"请求体: {json.dumps(kwargs['json'], indent=2, ensure_ascii=False)}")
if 'params' in kwargs:
logger.info(f"请求参数: {kwargs['params']}")
# 发送请求
response = self.session.request(method, full_url, **kwargs)
# 记录响应信息
logger.info(f"响应状态码: {response.status_code}")
try:
logger.info(f"响应体: {json.dumps(response.json(), indent=2, ensure_ascii=False)}")
except json.JSONDecodeError:
logger.info(f"响应体(非JSON): {response.text}")
# 返回一个统一的响应对象,包含更多有用信息
return {
"status_code": response.status_code,
"headers": response.headers,
"body": response.json() if 'application/json' in response.headers.get('Content-Type', '') else response.text,
"original_response": response # 保留原始响应对象以备不时之需
}
# 定义便捷方法
def get(self, url, params=None, **kwargs):
return self._request('GET', url, params=params, **kwargs)
def post(self, url, json_data=None, **kwargs):
return self._request('POST', url, json=json_data, **kwargs)
def put(self, url, json_data=None, **kwargs):
return self._request('PUT', url, json=json_data, **kwargs)
def delete(self, url, **kwargs):
return self._request('DELETE', url, **kwargs)
创建 common/logger.py :
import logging
import os
def get_logger(name, level=logging.INFO):
"""创建一个格式化的logger"""
logger = logging.getLogger(name)
logger.setLevel(level)
# 避免重复添加handler
if not logger.handlers:
# 创建控制台handler
ch = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
ch.setFormatter(formatter)
logger.addHandler(ch)
return logger
第二步:配置管理——告别硬编码
目标:将环境配置(如测试/生产环境URL)与代码分离。
创建 config/config.py :
import os
class Config:
"""配置类,支持从环境变量或默认值读取配置"""
# 基础URL,可通过环境变量覆盖
BASE_URL = os.getenv("API_BASE_URL", "https://api.your-test-server.com/v1")
# 数据库配置(如需)
DB_HOST = os.getenv("DB_HOST", "localhost")
DB_USER = os.getenv("DB_USER", "test_user")
config = Config()
第三步:测试数据分离——实现数据驱动
目标:将测试用例与测试数据分离,使用例更简洁,数据易于维护。
创建 test_data/user_data.py :
# 正常用例数据
VALID_USER_DATA = {
"create_user": {"name": "悟空", "email": "wukong@xyj.com"},
"login_success": {"username": "admin", "password": "123456"}
}
# 异常用例数据
INVALID_USER_DATA = {
"login_missing_password": {"username": "admin"},
"login_wrong_password": {"username": "admin", "password": "wrong"}
}
第四步:编写真正的测试用例——使用Pytest
目标:利用Pytest框架的强大功能(夹具、参数化、断言)编写清晰、健壮的测试。
创建 test_cases/test_user_api.py :
import pytest
import allure
from config.config import config
from common.request_client import RequestClient
# 使用pytest fixture在用例开始时自动初始化客户端
@pytest.fixture(scope="class")
def api_client():
client = RequestClient(base_url=config.BASE_URL)
yield client # 提供客户端给测试类使用
# 测试结束后可以在这里做一些清理工作,比如退出登录
@allure.epic("用户管理模块")
@allure.feature("用户登录")
class TestUserLogin:
"""测试用户登录相关接口"""
@allure.story("成功登录")
@allure.title("使用正确的用户名和密码可以成功登录")
def test_login_success(self, api_client):
# 准备测试数据
login_data = {"username": "admin", "password": "123456"}
# 执行请求
response = api_client.post("/auth/login", json_data=login_data)
# 断言
assert response["status_code"] == 200
assert response["body"]["code"] == 0
assert "token" in response["body"]["data"]
assert response["body"]["data"]["username"] == login_data["username"]
@allure.story("登录失败")
@allure.title("密码错误时登录失败")
@pytest.mark.parametrize("case_name, login_data, expected_code, expected_msg", [
("缺失密码", {"username": "admin"}, 400, "密码不能为空"),
("密码错误", {"username": "admin", "password": "wrong"}, 400, "用户名或密码错误"),
])
def test_login_failure(self, api_client, case_name, login_data, expected_code, expected_msg):
with allure.step(f"测试场景:{case_name}"):
response = api_client.post("/auth/login", json_data=login_data)
assert response["status_code"] == expected_code
assert response["body"]["msg"] == expected_msg
@allure.feature("用户CRUD")
class TestUserCRUD:
"""测试用户的增删改查"""
def test_create_user(self, api_client):
user_data = {"name": "新用户", "email": "new@example.com"}
response = api_client.post("/users", json_data=user_data)
assert response["status_code"] == 201
created_user = response["body"]["data"]
assert created_user["name"] == user_data["name"]
# 通常这里会保存创建的用户ID,用于后续的查询、更新、删除测试
# 这体现了用例之间的依赖和数据传递
return created_user["id"]
# 这个测试依赖于上一个测试创建的用户
def test_get_user_by_id(self, api_client):
# 在实际框架中,这里应该获取上一步创建的user_id
# user_id = self.test_create_user(api_client)
user_id = 123 # 假设一个存在的ID,或从配置中读取
response = api_client.get(f"/users/{user_id}")
assert response["status_code"] == 200
第五步:运行与报告——让结果一目了然
创建 run_tests.py :
import pytest
import os
import shutil
if __name__ == "__main__":
# 清理之前的报告
report_dir = "./reports"
if os.path.exists(report_dir):
shutil.rmtree(report_dir)
# 运行pytest,生成Allure原始数据
# 1. 运行测试
pytest.main(["-v", "-s", "test_cases/", "--alluredir=./reports/allure_raw"])
# 2. 生成HTML报告(需要先安装Allure命令行工具)
# 你可以在CI/CD中执行这一步
os.system("allure generate ./reports/allure_raw -o ./reports/allure_html --clean")
print("测试完成!打开 `reports/allure_html/index.html` 查看精美报告。")
第三部分:框架的高阶扩展能力
你的框架已经具备雏形,但它的强大之处在于可扩展性:
- 全局认证处理:在
RequestClient中自动为所有请求添加Token。 - 数据驱动
@pytest.mark.parametrize:从Excel、JSON或YAML文件中读取大量测试数据。 - 数据库校验:接口调用后,自动查询数据库验证数据是否正确落库。
- Mock服务:在依赖服务不稳定时,使用
unittest.mock或pytest-mock进行隔离测试。 - CI/CD集成:在Jenkins/GitLab CI中一键执行测试,并将Allure报告发布到服务器。
总结:从Postman到专业框架的蜕变
通过以上步骤,你已经不再是一个简单的API调用者,而是一个自动化框架的设计师。
| 特性 | Postman | Python + Requests 框架 |
|---|---|---|
| 维护性 | 集合/环境变量,协作尚可 | 代码版本控制,极致协作 |
| 灵活性 | GUI操作,有限 | 代码实现,无限可能 |
| 扩展性 | 有限,依赖Newman等CLI工具 | 极强,无缝接入Python生态 |
| 报告 | 基础HTML报告 | Allure等专业级可视化报告 |
| 集成性 | 需要额外配置 | 天生为CI/CD设计 |
更多推荐



所有评论(0)