
Python+Requests零基础系统掌握接口自动化测试---youkeit.xyz/1008/
从Postman到Python+Requests:接口自动化"脚本化"技术跃迁实战指南
引言:接口测试的进化之路
随着敏捷开发和DevOps的普及,接口测试从简单的工具点击操作,逐步演进为可编程的自动化测试体系。本文将带你完成从Postman可视化操作到Python+Requests全脚本化开发的完整跃迁,通过对比展示和代码实战,揭示自动化测试的核心技术升级路径。
一、Postman基础:可视化接口测试
1.1 Postman基本工作流
javascript
// Postman的Tests脚本示例
pm.test("状态码应为200", function() {
pm.response.to.have.status(200);
});
pm.test("响应时间小于200ms", function() {
pm.expect(pm.response.responseTime).to.be.below(200);
});
pm.test("包含特定字段", function() {
var jsonData = pm.response.json();
pm.expect(jsonData.data).to.have.property('username');
});
1.2 Postman局限性分析
- 难以版本控制:测试用例存储在Postman集合中
- 有限编程能力:仅支持JavaScript片段
- 不易集成:与CI/CD流水线集成复杂
二、Python+Requests基础迁移
2.1 基础请求转换
Postman中的GET请求转换为Python:
python
import requests
# 对应Postman GET请求
response = requests.get(
'https://api.example.com/users',
params={'page': 1, 'limit': 20},
headers={'Authorization': 'Bearer token123'}
)
# 断言状态码
assert response.status_code == 200
# 断言响应时间
assert response.elapsed.total_seconds() * 1000 < 200
# 断言JSON字段
assert 'username' in response.json()['data']
2.2 POST请求示例
python
# 复杂POST请求示例
auth_payload = {
'username': 'testuser',
'password': 'Test@123'
}
login_response = requests.post(
'https://api.example.com/auth/login',
json=auth_payload,
headers={'Content-Type': 'application/json'}
)
# 获取token供后续使用
auth_token = login_response.json()['token']
三、高级封装与设计模式
3.1 请求封装类
python
class APIClient:
def __init__(self, base_url):
self.base_url = base_url
self.session = requests.Session()
self.default_headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
def _request(self, method, endpoint, **kwargs):
url = f"{self.base_url}{endpoint}"
headers = {**self.default_headers, **kwargs.pop('headers', {})}
response = self.session.request(
method=method,
url=url,
headers=headers,
**kwargs
)
response.raise_for_status() # 自动处理4xx/5xx错误
return response
def get(self, endpoint, params=None, **kwargs):
return self._request('GET', endpoint, params=params, **kwargs)
def post(self, endpoint, json=None, **kwargs):
return self._request('POST', endpoint, json=json, **kwargs)
# 可继续添加put/patch/delete等方法
3.2 测试用例组织
python
import unittest
class UserAPITests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.client = APIClient('https://api.example.com')
cls.client.default_headers.update({
'Authorization': f'Bearer {get_auth_token()}'
})
def test_get_user_list(self):
response = self.client.get('/users', params={'page': 1})
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertIsInstance(data['items'], list)
self.assertLessEqual(len(data['items']), 20)
def test_create_user(self):
user_data = {
'username': 'new_user',
'email': 'new@example.com',
'password': 'P@ssw0rd'
}
response = self.client.post('/users', json=user_data)
self.assertEqual(response.status_code, 201)
self.assertIn('id', response.json())
四、Postman到Python的自动化转换
4.1 Postman集合导出
- 在Postman中选择集合 → 导出 → 选择v2.1格式
- 保存为JSON文件(如
postman_collection.json)
4.2 自动转换脚本
python
import json
from pathlib import Path
def convert_postman_to_python(collection_path, output_dir):
with open(collection_path) as f:
collection = json.load(f)
output_dir = Path(output_dir)
output_dir.mkdir(exist_ok=True)
for item in collection['item']:
class_name = item['name'].replace(' ', '')
filename = f"test_{class_name.lower()}.py"
with open(output_dir / filename, 'w') as f:
f.write(f"import unittest\n")
f.write(f"from api_client import APIClient\n\n")
f.write(f"class {class_name}Tests(unittest.TestCase):\n")
f.write(f" @classmethod\n def setUpClass(cls):\n")
f.write(f" cls.client = APIClient('{collection.get('variable', [{}])[0].get('value', '')}')\n\n")
for request in item.get('item', []):
method = request['request']['method'].lower()
test_name = request['name'].replace(' ', '_').lower()
url = request['request']['url'].get('raw', '').split('?')[0]
f.write(f" def test_{test_name}(self):\n")
if method == 'get':
f.write(f" response = self.client.{method}('{url}')\n")
else:
f.write(f" payload = {request['request'].get('body', {}).get('raw', '{}')}\n")
f.write(f" response = self.client.{method}('{url}', json=payload)\n")
f.write(f" self.assertEqual(response.status_code, 200)\n\n")
f.write(f"if __name__ == '__main__':\n unittest.main()\n")
# 使用示例
convert_postman_to_python('postman_collection.json', 'generated_tests')
五、进阶自动化能力
5.1 数据驱动测试
python
import csv
import ddt
@ddt.ddt
class DataDrivenTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.client = APIClient('https://api.example.com')
@ddt.file_data('test_data.json')
def test_create_user(self, username, email, password):
user_data = {
'username': username,
'email': email,
'password': password
}
response = self.client.post('/users', json=user_data)
self.assertEqual(response.status_code, 201)
self.assertTrue(response.json()['id'])
5.2 自动化断言生成
python
def generate_assertions(response, prefix=''):
assertions = []
if isinstance(response, dict):
for key, value in response.items():
new_prefix = f"{prefix}['{key}']" if prefix else f"['{key}']"
if isinstance(value, (dict, list)):
assertions.extend(generate_assertions(value, new_prefix))
else:
assertions.append(f"self.assertIn('{key}', response.json(){prefix})")
elif isinstance(response, list) and len(response) > 0:
assertions.extend(generate_assertions(response[0], f"{prefix}[0]"))
return assertions
# 使用示例
response = requests.get('https://api.example.com/users/1')
print("\n".join(generate_assertions(response.json())))
六、CI/CD集成实战
6.1 pytest集成示例
python
# conftest.py
import pytest
@pytest.fixture(scope="module")
def api_client():
client = APIClient('https://api.example.com')
yield client
client.session.close()
# test_users.py
def test_user_flow(api_client):
# 创建用户
create_resp = api_client.post('/users', json={
'username': 'testuser',
'password': 'Test@123'
})
assert create_resp.status_code == 201
user_id = create_resp.json()['id']
# 查询用户
get_resp = api_client.get(f'/users/{user_id}')
assert get_resp.status_code == 200
assert get_resp.json()['username'] == 'testuser'
# 删除用户
del_resp = api_client.delete(f'/users/{user_id}')
assert del_resp.status_code == 204
6.2 GitHub Actions配置
yaml
name: API Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.9'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests pytest pytest-cov
- name: Run tests
run: |
pytest tests/ --cov=src --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v1
七、企业级最佳实践
7.1 测试框架扩展
python
# base_test.py
class APITestBase(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.client = APIClient(os.getenv('API_BASE_URL'))
cls.test_data = load_test_data()
def assertResponseTime(self, response, max_ms):
elapsed_ms = response.elapsed.total_seconds() * 1000
self.assertLessEqual(elapsed_ms, max_ms,
f"响应时间{elapsed_ms}ms超过限制{max_ms}ms")
def assertSchema(self, response, schema):
jsonschema.validate(response.json(), schema)
# 继承使用
class ProductTests(APITestBase):
def test_product_list(self):
response = self.client.get('/products')
self.assertEqual(response.status_code, 200)
self.assertResponseTime(response, 500)
self.assertSchema(response, PRODUCT_SCHEMA)
7.2 智能Mock服务
python
from unittest.mock import patch
class MockTests(unittest.TestCase):
@patch('requests.Session.request')
def test_mocked_api(self, mock_request):
# 配置Mock响应
mock_response = unittest.mock.Mock()
mock_response.status_code = 200
mock_response.json.return_value = {'id': 123, 'name': 'Mocked'}
mock_request.return_value = mock_response
# 执行测试
client = APIClient('http://mocked')
response = client.get('/users/123')
# 验证
self.assertEqual(response.status_code, 200)
self.assertEqual(response.json()['name'], 'Mocked')
mock_request.assert_called_with(
'GET', 'http://mocked/users/123',
headers={'Accept': 'application/json', 'Content-Type': 'application/json'},
allow_redirects=True, params=None, timeout=None
)
结语:技术跃迁的价值提升
从Postman到Python+Requests的转变,不仅仅是工具的更换,更是测试思维和技术能力的全面升级:
- 可编程性:完整Python生态支持复杂测试逻辑
- 可维护性:代码化用例更易于版本管理和团队协作
- 可扩展性:轻松集成到CI/CD流程和企业测试体系
- 可复用性:通过封装实现测试资产积累
关键建议:初期可以Postman作为探索性测试工具,待接口稳定后转换为Python自动化脚本,两者互补而非互斥。最终目标是建立"代码即文档、测试即资产"的现代化接口测试体系。