Python+Requests零基础系统掌握接口自动化测试

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集合导出

  1. 在Postman中选择集合 → 导出 → 选择v2.1格式
  2. 保存为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的转变,不仅仅是工具的更换,更是测试思维和技术能力的全面升级:

  1. 可编程性:完整Python生态支持复杂测试逻辑
  2. 可维护性:代码化用例更易于版本管理和团队协作
  3. 可扩展性:轻松集成到CI/CD流程和企业测试体系
  4. 可复用性:通过封装实现测试资产积累

关键建议:初期可以Postman作为探索性测试工具,待接口稳定后转换为Python自动化脚本,两者互补而非互斥。最终目标是建立"代码即文档、测试即资产"的现代化接口测试体系。

相关推荐
肖文英2 小时前
Java类型概览
后端
武子康3 小时前
大数据-144 Apache Kudu:实时写 + OLAP 的架构、性能与集成
大数据·后端·nosql
程序员小假3 小时前
设计模式了解吗,知道什么是饿汉式和懒汉式吗?
java·后端
golang学习记3 小时前
Spring Boot 4.0官宣: 弃用 Undertow:Tomcat笑麻了
后端
林太白3 小时前
rust-Serialize序列和反序列Deserialize
后端·rust
用户68545375977693 小时前
🌐 分布式算法:限流、负载均衡、分布式ID,实战必备!
后端
后端小张3 小时前
【JAVA 进阶】穿越之我在修仙世界学习 @Async 注解(深度解析)
java·开发语言·spring boot·后端·spring·注解·原理
Yeats_Liao3 小时前
Go Web 编程快速入门 18 - 附录B:查询与扫描
开发语言·前端·后端·golang
国服第二切图仔3 小时前
Rust实战开发之图形界面开发入门(egui crate)
开发语言·后端·rust