Python接口自动化测试脚本编写:基于requests库实战
本文基于Python的requests库,从基础调用、参数组织、响应断言到Session状态保持与模块化脚本封装,提供完整详实的接口自动化测试脚本实战指南。
接口自动化测试旨在通过编写程序替代人工点击,对服务端的应用程序接口(API)进行自动化调用与结果校验。Python的requests库因其简洁优雅的API设计,成为了编写HTTP接口测试脚本的首选基础库。本文将围绕requests库的核心功能,从环境准备、常用请求方法封装、测试用例断言编写,到会话保持与分层脚本设计,通过具体的代码示例逐步讲解如何完成一套实用的接口测试脚本。
一、自动化测试核心交互流程
在编写测试脚本前,需要明确自动化测试运行的生命周期。与浏览器行为类似,测试脚本充当HTTP客户端,构造请求报文发送至目标服务器,接收到响应报文后,解析状态码与数据体,并与预期结果进行比对,最终输出断言结论。
二、测试环境准备与基础调用
编写脚本前需安装requests模块。建议在Python虚拟环境中通过pip安装:
pip install requests
完成安装后,首先编写一个最简的GET请求,验证目标接口的基础连通性并检查响应内容:
import requests
# 目标接口地址(以公网回显测试接口为例)
base_url = "https://httpbin.org/get"
# 构造请求查询参数
query_params = {"category": "book", "page": 1}
# 发送GET请求并设置超时时间(单位:秒)
response = requests.get(url=base_url, params=query_params, timeout=5)
# 输出状态码与响应体
print("Status Code:", response.status_code)
print("Response JSON:", response.json())
requests.get()或requests.post()时,都必须显式指定timeout参数。如果不设置超时时间,当目标服务器挂起或网络故障时,脚本将一直阻塞,导致整个测试集运行卡死。
三、常见请求方法与传参差异对比
在接口自动化中,最常见的请求是GET与POST。许多初学者容易混淆params、data和json三个传参参数的区别。理解它们的HTTP报文差异是写出可靠自动化用例的关键:
params:用于将字典编码为查询字符串附加在URL后(如?k=v),主要用于GET请求。data:用于向请求体提交表单数据(Content-Type: application/x-www-form-urlencoded)。json:用于将Python字典自动序列化为JSON字符串,并自动设置头信息为Content-Type: application/json。当前大部分RESTful API均采用此格式。
下面演示发送POST请求提交JSON数据的完整写法:
payload = {
"username": "tester_alice",
"email": "alice@example.com",
"role": "QA"
}
headers = {
"User-Agent": "ApiTestBot/1.0"
}
response = requests.post(
url="https://httpbin.org/post",
json=payload,
headers=headers,
timeout=5
)
data = response.json()
print("服务端解析到的JSON:", data["json"])
四、自动化测试用例编写与多维度断言
自动化测试脚本与普通爬虫脚本的最大区别在于断言(Assertion)。脚本必须能够自动判断结果是否符合预期。在接口测试中,断言通常由外到内分为三个层级:
- HTTP状态码断言:确认HTTP连接与路由正常,如断言状态码为200、201或204。
- 业务状态码与消息断言:在响应JSON中确认业务字段,如
code == 0或message == "success"。 - 业务实体数据精确断言:验证返回的数据内容与输入或数据库记录是否一致。
以下为一个完整的测试函数示例,模拟用户登录接口的断言检验:
def test_login_success():
login_url = "https://httpbin.org/post"
body = {
"username": "admin",
"password": "correct_password"
}
response = requests.post(login_url, json=body, timeout=5)
# 1. 断言 HTTP 状态码
assert response.status_code == 200, f"期望状态码200,实际返回: {response.status_code}"
# 2. 解析 JSON
result = response.json()
# 3. 断言提交的业务参数完整性
json_body = result.get("json", {})
assert json_body.get("username") == "admin", "响应中用户名与提交不一致"
# 4. 断言响应头信息
assert "application/json" in response.headers.get("Content-Type", ""), "响应类型非JSON"
print("test_login_success: 测试通过")
if __name__ == "__main__":
test_login_success()
五、基于 Session 的上下文状态与鉴权保持
在真实的业务场景中,很多接口必须在登录后才能调用。如果每次都使用独立的requests.post()调用,每次连接都会创建新的TCP连接,且不会自动携带上一个请求返回的Cookie或Token。
requests.Session()对象提供了跨请求保持Cookie的能力,同时它还支持在Session级别配置公共Headers(如统一的Authorization头),避免在每个请求中重复传入。
Session的典型代码写法如下:
# 实例化会话对象
session = requests.Session()
# 配置全局通用请求头
session.headers.update({
"User-Agent": "AutomationTestRunner/1.0",
"Accept": "application/json"
})
# 1. 模拟登录,服务端返回 Set-Cookie
session.get("https://httpbin.org/cookies/set?token=secret_123456", timeout=5)
# 2. 访问受保护接口,Session 会自动携带上述 Cookie
profile_res = session.get("https://httpbin.org/cookies", timeout=5)
cookies_in_server = profile_res.json().get("cookies", {})
# 3. 校验状态保持
assert cookies_in_server.get("token") == "secret_123456", "Cookie 会话未成功保持"
print("会话保持验证成功!")
六、脚本工程化:模块化封装与实战演练
直接在测试脚本中散落编写requests.get或硬编码URL,会导致测试代码重复度高、后期维护困难。当接口地址发生变动或鉴权方式升级时,修改成本极高。工程化的做法是将接口调用逻辑封装为客户端类(Client),测试用例只负责构造测试数据和执行断言。
下面是一个可直接用于实际项目的轻量级封装示例:
import requests
class BaseApiClient:
"""通用接口基础类,维护 Session 和基础 URL"""
def __init__(self, base_url: str):
self.base_url = base_url.rstrip("/")
self.session = requests.Session()
self.session.headers.update({"Accept": "application/json"})
def request(self, method: str, endpoint: str, **kwargs) -> requests.Response:
url = f"{self.base_url}/{endpoint.lstrip('/')}"
# 设置默认超时时间 5 秒
kwargs.setdefault("timeout", 5)
response = self.session.request(method=method, url=url, **kwargs)
return response
class UserApiClient(BaseApiClient):
"""业务接口类,封装具体业务方法"""
def create_user(self, name: str, job: str) -> requests.Response:
return self.request("POST", "/post", json={"name": name, "job": job})
def get_user_list(self, page: int = 1) -> requests.Response:
return self.request("GET", "/get", params={"page": page})
# --- 测试用例函数 ---
def test_user_lifecycle():
client = UserApiClient(base_url="https://httpbin.org")
# 执行创建用户接口测试
create_res = client.create_user(name="Bob", job="Engineer")
assert create_res.status_code == 200, f"创建用户失败,状态码: {create_res.status_code}"
json_data = create_res.json().get("json", {})
assert json_data.get("name") == "Bob", "返回的姓名不符合预期"
assert json_data.get("job") == "Engineer", "返回的岗位不符合预期"
# 执行获取用户列表测试
list_res = client.get_user_list(page=2)
assert list_res.status_code == 200
assert list_res.json().get("args", {}).get("page") == "2"
print("用户全生命周期接口测试通过!")
if __name__ == "__main__":
test_user_lifecycle()
七、常见问题排查与编写注意事项
1. JSONDecodeError 异常:当接口返回的不是合法 JSON(例如服务器报 502 错误返回 HTML 页面,或返回空响应体)时,调用 response.json() 会直接抛出异常。编写脚本时应先校验 response.status_code == 200,或者在断言前检查返回的 Content-Type。
2. 字典键不存在 KeyError:在解析嵌套较深的 JSON 结构时,直接使用 data['a']['b'] 容易在字段缺失时导致测试脚本崩溃。推荐使用 dict.get('key', default) 获取字段并进行判断,给出明确的错误提示。
3. HTTPS 证书校验问题:在内网测试环境中,如果使用的是自签名证书,requests 可能会抛出 SSLError。可通过在请求中设置 verify=False 忽略证书校验,但需注意在生产环境应保持默认的安全校验。
通过本文的实战演练,从底层的HTTP请求组装,到Session层面的连接和状态维护,再到业务对象的面向对象封装,读者可以建立起编写Python接口测试脚本的完整体系。后续可进一步结合pytest测试框架实现测试用例自动发现、参数化驱动以及测试报告生成。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









