python接口自动化梳理
一、接口自动化流程梳理
1.框架选择
主流框架对比
(1)Requests + Pytest (最常用)
(2)Robot Framework (关键字驱动)
(3)HttpRunner (专为HTTP接口测试)
(4)Unittest + Requests (Python标准库)
推荐:Requests + Pytest组合 - 灵活性强,社区活跃,插件丰富
2.项目结构设计
api_auto_test/
├── config/ # 配置文件
│ ├── __init__.py
│ ├── config.py # 基本配置
│ └── endpoints.py # 接口端点配置
├── common/ # 公共模块
│ ├── __init__.py
│ ├── logger.py # 日志模块
│ ├── request_client.py # 请求客户端
│ └── assert_utils.py # 断言工具
├── test_cases/ # 测试用例
│ ├── __init__.py
│ ├── test_user_api.py # 用户相关测试
│ └── test_product_api.py # 产品相关测试
├── test_data/ # 测试数据
│ ├── user_data.py
│ └── product_data.py
├── reports/ # 测试报告
├── conftest.py # Pytest配置
├── requirements.txt # 依赖包
└── run_tests.py # 测试执行入口
3.设计一个完整的接口自动化测试框架,需要考虑哪些方面?
框架设计要素:
(1) 架构设计:分层设计(用例层、业务层、数据层)
(2) 配置管理:多环境支持、敏感信息管理
(3) 用例管理:用例组织、标签管理、依赖处理
(4)数据管理:测试数据生成、数据驱动、数据清理
(5)报告体系:测试报告、日志记录、失败截图
(6)异常处理:重试机制、超时控制、错误恢复
(7)持续集成:CI/CD集成、定时执行、质量门禁
framework/
├── core/ # 核心模块
├── utils/ # 工具类
├── testcases/ # 测试用例
├── config/ # 配置管理
├── data/ # 测试数据
├── reports/ # 测试报告
└── ci/ # CI/CD配置
二、pytest总结
1.fixture作用:
(1) 测试前置和后置操作
(2)测试数据准备
(3) 依赖注入
(4) 资源共享(通过scope控制)
import pytest
@pytest.fixture(scope="function") # 默认:每个测试函数执行一次
def func_scope():return "function_scope"
@pytest.fixture(scope="class") # 每个测试类执行一次
def class_scope():return "class_scope"
@pytest.fixture(scope="module") # 每个模块执行一次
def module_scope():return "module_scope"
@pytest.fixture(scope="session") # 整个测试会话执行一次
def session_scope():return "session_scope"
import pytest
@pytest.fixture(scope="function")
def setup_data():"""前置操作"""data = {"user": "test", "id": 1}yield data # 测试用例执行# 后置清理操作print("清理测试数据")
def test_example(setup_data):assert setup_data["user"] == "test"
2.如何处理接口依赖?比如B接口需要A接口返回的token
import pytest
class TestDependentAPI:@pytest.fixturedef get_auth_token(self):"""获取认证token"""response = requests.post("/login", json=credentials)return response.json()["token"]def test_dependent_api(self, get_auth_token):"""依赖token的接口测试"""headers = {"Authorization": f"Bearer {get_auth_token}"}response = requests.get("/user/profile", headers=headers)assert response.status_code == 200
3.实现数据驱动-参数化
(1)使用pytest参数化
import pytest
test_data = [("admin", "admin123", 200, "登录成功"),("admin", "wrong_pwd", 401, "密码错误"),("", "admin123", 400, "用户名为空")
]@pytest.mark.parametrize("username,password,expected_code,desc", test_data)
def test_login(username, password, expected_code, desc):data = {"username": username, "password": password}response = requests.post("/login", json=data)assert response.status_code == expected_code
#基本参数化
import pytest
@pytest.mark.parametrize("input,expected", [(1, 2),(2, 4),(3, 6)
])
def test_double(input, expected):assert input * 2 == expected
# 多个参数组合
@pytest.mark.parametrize("x", [1, 2])
@pytest.mark.parametrize("y", [10, 20])
def test_combinations(x, y):assert x + y == x + y # 会产生4个测试用例
(2)使用外部文件驱动
import json
import csv# JSON数据驱动
def load_json_testdata(file_path):with open(file_path, 'r', encoding='utf-8') as f:return json.load(f)
# 例子1
import json
def load_test_data():with open('test_data.json') as f:return json.load(f)@pytest.mark.parametrize("data", load_test_data())
def test_with_external_data(data):assert data["input"] * 2 == data["expected"]
# CSV数据驱动
def load_csv_testdata(file_path):test_cases = []with open(file_path, 'r', encoding='utf-8') as f:reader = csv.DictReader(f)for row in reader:test_cases.append(row)return test_cases
4.如何Mock外部依赖接口
import pytest
from unittest.mock import Mock, patchclass TestWithMock:def test_with_mocked_external_api(self, mocker):# Mock外部API响应mock_response = Mock()mock_response.status_code = 200mock_response.json.return_value = {"result": "success"}# 替换requests.get方法mocker.patch('requests.get', return_value=mock_response)# 执行测试result = external_api_call()assert result == "success"# 使用patch装饰器
@patch('module.requests.get')
def test_decorator_mock(mock_get):mock_get.return_value.status_code = 200# 测试逻辑
5.Pytest如何自动发现测试用例
# 默认发现规则:
# 1. 文件名:test_*.py 或 *_test.py
# 2. 类名:Test开头(无__init__方法)
# 3. 方法名:test_开头# 示例:
# test_example.py
class TestCalculator:def test_addition(self): # 会被发现assert 1 + 1 == 2def addition_test(self): # 不会被发现assert 2 + 2 == 4
6.Pytest与Unittest的主要区别
特性 Pytest Unittest
语法 简洁,使用assert 需要self.assertEqual()
夹具 fixture机制更灵活 setUp/tearDown
参数化 @pytest.mark.parametrize 需要subTest或第三方库
插件 丰富的插件生态 相对较少
发现机制 自动发现测试文件 需要继承TestCase
7.Pytest的断言机制有什么特点
# 直接使用Python的assert语句
def test_basic_assert():assert 1 == 1assert Trueassert not False# 复杂断言
def test_complex_assert():# 列表断言assert [1, 2, 3] == [1, 2, 3]# 包含断言assert "hello" in "hello world"# 异常断言with pytest.raises(ValueError):int("invalid")# 类型断言assert isinstance("hello", str)
8.常用插件
(1)pip install pytest-html # HTML报告
(2)pip install pytest-xdist # 分布式测试
(3)pip install pytest-cov # 覆盖率测试
(4)pip install pytest-mock # Mock支持
(5)pip install pytest-timeout # 超时控制
(6)pip install pytest-rerunfailures # 失败重试
(7)pip install allure-pytest # Allure报告
9.高级特性
(1)跳过测试
import pytest
import sys
@pytest.mark.skip(reason="功能尚未实现")
def test_skip():assert False
@pytest.mark.skipif(sys.version_info < (3, 8), reason="需要Python 3.8+")
def test_skipif():assert True(2)预期失败
@pytest.mark.xfail(reason="已知问题,预期失败")
def test_expected_failure():assert False # 预期失败,不会标记为失败
@pytest.mark.xfail(run=False, reason="不执行此测试")
def test_not_run():assert False # 不会执行(3)测试失败重试
# 使用pytest-rerunfailures插件
@pytest.mark.flaky(reruns=3, reruns_delay=2) # 重试3次,间隔2秒
def test_flaky_api():response = unreliable_api_call()assert response.status_code == 200# 命令行方式
# pytest --reruns 3 --reruns-delay 2 test_file.py
# 自定义重试逻辑
def test_with_retry_logic():max_retries = 3for attempt in range(max_retries):try:result = flaky_operation()assert result is Truebreakexcept AssertionError:if attempt == max_retries - 1:raisetime.sleep(1)
10.生成专业的测试报告
# 1. HTML报告
pytest --html=report.html --self-contained-html
# 2. Allure报告
pytest --alluredir=allure-results
allure serve allure-results
# 3. JUnit XML报告(用于CI/CD)
pytest --junitxml=report.xml
# 4. 自定义报告插件
```python
def pytest_terminal_summary(terminalreporter, exitstatus, config):"""终端报告摘要"""if exitstatus == 0:terminalreporter.write_sep("=", "测试通过!", green=True)else:terminalreporter.write_sep("=", "测试失败!", red=True)
# 5. 测试覆盖率报告
#pytest --cov=myproject --cov-report=html
11.常见的Pytest测试模式
# 1. 运行特定标记的测试
pytest -m "smoke" # 只运行冒烟测试
pytest -m "not slow" # 排除慢测试
# 2. 运行特定文件或目录
pytest tests/unit/ # 运行整个目录
pytest tests/test_api.py::TestLogin # 运行特定类
pytest tests/test_api.py::TestLogin::test_login_success # 运行特定方法
# 3. 失败时进入PDB调试
pytest --pdb # 失败时进入调试
pytest --trace # 立即进入调试
# 4. 测试执行控制
pytest -x # 遇到第一个失败就停止
pytest --maxfail=3 # 最多失败3个就停止
pytest --stepwise # 从上一次失败的地方开始
# 5. 性能分析
pytest --durations=10 # 显示最慢的10个测试