☰
Python Requests模块在API测试中的高效应用
2026/10/26 14:26:42 网站建设 项目流程

1. 为什么选择Requests模块进行API测试

在Python生态中,Requests是当之无愧的HTTP客户端库首选。根据PyPI官方统计,Requests库的周下载量长期保持在8000万次以上,这个数字远超同类库。我从业十年间见证了这个库从诞生到成为行业标准的过程,它用简洁优雅的API设计征服了无数开发者。

与标准库urllib相比,Requests的代码可读性提升明显。比如发送一个带参数的GET请求,urllib需要处理URL编码、连接池等底层细节:

from urllib.parse import urlencode from urllib.request import urlopen params = urlencode({'key1': 'value1', 'key2': 'value2'}) response = urlopen(f"http://example.com/api?{params}")

而用Requests只需一行:

response = requests.get('http://example.com/api', params={'key1': 'value1', 'key2': 'value2'})

这种人性化设计在复杂场景下优势更明显。去年我在某金融项目对接第三方支付API时,需要处理OAuth2认证、异常重试、文件上传等复杂需求,Requests的链式调用让代码保持整洁,而用urllib的话代码量至少增加三倍。

提示:虽然Python 3标准库的http.client也能完成类似工作,但缺乏连接池管理、超时重试等企业级功能,生产环境强烈建议使用Requests。

2. 环境准备与请求基础

2.1 安装配置最佳实践

安装Requests看似简单,但有些细节决定成败。建议使用虚拟环境隔离项目依赖:

python -m venv api_test_env source api_test_env/bin/activate # Linux/Mac api_test_env\Scripts\activate.bat # Windows pip install requests==2.31.0 # 指定稳定版本

我遇到过因版本差异导致的坑:某次升级后,Requests 3.x修改了默认的URL编码规则,导致老项目中的签名校验全部失败。所以生产环境务必固定版本号。

验证安装是否成功:

import requests print(requests.__version__) # 应显示2.31.0

2.2 HTTP请求方法实战

不同HTTP方法在API测试中的使用场景:

方法典型场景Requests示例
GET查询资源requests.get(url, params=params)
POST创建资源/提交数据requests.post(url, json=data)
PUT全量更新资源requests.put(url, data=payload)
PATCH部分更新资源requests.patch(url, json=updates)
DELETE删除资源requests.delete(url)

重点说下POST的两种传参方式区别:

  • data=dict:发送form表单格式,Content-Type为application/x-www-form-urlencoded
  • json=dict:发送JSON格式,Content-Type为application/json

去年测试某电商平台API时,错误使用data传JSON导致服务端解析失败。正确的做法是:

# 错误示例(服务端收不到正确数据) requests.post(url, data={'item': 123}) # 正确做法 requests.post(url, json={'item': 123})

3. 高级功能与实战技巧

3.1 会话保持与性能优化

单个请求的简单场景直接调用requests.get()没问题,但测试完整业务流时需要会话保持。比如用户登录后执行系列操作:

with requests.Session() as s: # 登录(保持cookies) s.post(login_url, json=credentials) # 查询订单(自动携带认证信息) orders = s.get(order_url).json() # 支付操作 payment = s.post(pay_url, data=payment_data)

Session的另一个优势是连接池复用。我们做过压测:相同1000次请求,使用Session比单次请求快3倍以上,因为避免了重复TCP握手。

3.2 超时与重试机制

线上环境必须设置超时!我见过因未设超时导致测试进程卡死的案例:

# 危险:可能永久挂起 requests.get(unstable_api) # 安全做法(连接超时3秒,读取超时10秒) requests.get(unstable_api, timeout=(3, 10))

对于不稳定的测试环境,建议使用urllib3的Retry:

from urllib3.util.retry import Retry from requests.adapters import HTTPAdapter retry_strategy = Retry( total=3, backoff_factor=1, status_forcelist=[500, 502, 503, 504] ) adapter = HTTPAdapter(max_retries=retry_strategy) session = requests.Session() session.mount("https://", adapter)

这个配置会在服务端返回5xx错误时自动重试3次,每次间隔时间按指数增长(1s, 2s, 4s)。

4. 断言与测试框架集成

4.1 响应验证模式

完整的API测试应该包含这些断言点:

response = requests.get(api_url) # 状态码断言 assert response.status_code == 200 # 响应头验证 assert 'application/json' in response.headers['Content-Type'] # 响应体校验 data = response.json() assert data['status'] == 'success' assert len(data['items']) > 0 # 响应时间监控 assert response.elapsed.total_seconds() < 1.0

对于复杂JSON结构,推荐使用jsonpath提取数据:

from jsonpath_ng import parse def test_user_profile(): resp = requests.get(user_api) json_data = resp.json() # 使用jsonpath查找嵌套值 expr = parse("$.addresses[?(@.type=='office')].postcode") postcodes = [match.value for match in expr.find(json_data)] assert '100000' in postcodes

4.2 与pytest集成实战

将Requests测试封装成pytest测试类:

import pytest class TestUserAPI: @pytest.fixture def auth_headers(self): token = requests.post(login_url, json=creds).json()['token'] return {'Authorization': f'Bearer {token}'} def test_get_user(self, auth_headers): response = requests.get(user_url, headers=auth_headers) assert response.status_code == 200 assert response.json()['username'] == 'testuser' @pytest.mark.parametrize("user_id,expected", [ (1, 200), (999, 404), ("invalid", 400) ]) def test_user_status_codes(self, user_id, expected): response = requests.get(f"{user_url}/{user_id}") assert response.status_code == expected

这样可以通过pytest -v运行整套测试,并生成漂亮的测试报告。

5. 常见问题排查手册

5.1 SSL证书问题

当遇到SSLError时,通常有三种处理方式:

  1. 验证证书(推荐生产环境使用):
requests.get('https://example.com', verify='/path/to/cert.pem')
  1. 禁用验证(仅测试环境使用):
requests.get('https://example.com', verify=False)
  1. 忽略特定错误:
from requests.packages.urllib3.exceptions import InsecureRequestWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning)

5.2 代理配置技巧

公司内网常需要代理访问外网API:

proxies = { 'http': 'http://proxy.example.com:8080', 'https': 'http://proxy.example.com:8080', } requests.get('https://api.example.com', proxies=proxies)

如果需要NTLM认证的代理:

from requests_ntlm import HttpNtlmAuth proxies = {'http': 'http://proxy.example.com'} auth = HttpNtlmAuth('domain\\user', 'password') requests.get(url, proxies=proxies, auth=auth)

5.3 文件上传实战

测试文件上传接口时的正确姿势:

# 单文件上传 with open('report.pdf', 'rb') as f: files = {'document': ('quarterly_report.pdf', f, 'application/pdf')} r = requests.post(upload_url, files=files) # 多文件+表单数据 files = [ ('images', ('cat.png', open('cat.png', 'rb'), 'image/png')), ('images', ('dog.png', open('dog.png', 'rb'), 'image/png')) ] data = {'title': 'Pet photos'} requests.post(gallery_url, files=files, data=data)

注意文件句柄要用二进制模式('rb')打开,且Requests会自动设置正确的Content-Type。

6. 性能监控与Mock测试

6.1 接口性能指标采集

使用Requests结合time库进行简单性能测试:

import time def benchmark_api(url, iterations=100): times = [] for _ in range(iterations): start = time.perf_counter() requests.get(url) elapsed = time.perf_counter() - start times.append(elapsed) avg = sum(times) / len(times) print(f"Average response time: {avg:.3f}s") print(f"95th percentile: {sorted(times)[int(len(times)*0.95)]:.3f}s")

更专业的做法是用locust进行压力测试:

from locust import HttpUser, task class ApiUser(HttpUser): @task def get_users(self): self.client.get("/api/users") @task(3) # 3倍权重 def create_item(self): self.client.post("/api/items", json={"name": "test"})

6.2 使用responses进行Mock测试

单元测试应该避免真实网络请求:

import responses import unittest class TestMockAPI(unittest.TestCase): @responses.activate def test_user_api(self): # 模拟成功响应 responses.add( responses.GET, 'https://api.example.com/users/1', json={'id': 1, 'name': 'John'}, status=200 ) # 模拟错误响应 responses.add( responses.GET, 'https://api.example.com/users/999', json={'error': 'Not found'}, status=404 ) # 测试正常路径 resp = requests.get('https://api.example.com/users/1') self.assertEqual(resp.json()['name'], 'John') # 测试错误路径 resp = requests.get('https://api.example.com/users/999') self.assertEqual(resp.status_code, 404)

这种Mock方式可以精确控制返回内容,测试各种边界情况。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询