1. Python基础容器与文件操作实战指南
作为一名有多年Python开发经验的测试工程师,我经常需要处理各种数据结构和文件操作。Python的四大基础容器(列表、元组、字典、集合)和文件操作是自动化测试的基础技能。下面我将结合实战经验,详细解析这些核心知识点。
1.1 四大容器特性深度解析
1.1.1 列表(List) - 灵活的可变序列
列表是Python中最常用的数据结构之一,它的核心特点是:
- 有序集合,元素可重复
- 动态可变(增删改查)
- 支持索引和切片操作
实战技巧:
python复制# 初始化列表的几种方式
empty_list = []
num_list = [1, 2, 3] # 直接初始化
range_list = list(range(10)) # 通过range生成
str_list = list("hello") # 字符串转列表
# 列表推导式(高效创建列表)
squares = [x**2 for x in range(10) if x % 2 == 0]
关键方法解析:
insert(index, obj):在指定位置插入元素(时间复杂度O(n))append(obj):尾部追加元素(时间复杂度O(1))remove(obj):删除第一个匹配项(注意:元素不存在会报错)pop([index]):删除并返回指定位置元素(默认最后一个)sort(key=None, reverse=False):原地排序(返回None)
注意:列表的
+和*操作会创建新列表,频繁操作可能导致内存问题。对于大量数据追加,建议先用append然后一次性扩展。
1.1.2 元组(Tuple) - 不可变的轻量级容器
元组的特点:
- 不可变序列(创建后不能修改)
- 通常用于存储异构数据
- 比列表更节省内存
典型应用场景:
python复制# 作为函数返回值
def get_user_info():
return ("张三", 25, "男")
# 作为字典键(因为不可变)
locations = {
(40.7128, -74.0060): "纽约",
(51.5074, -0.1278): "伦敦"
}
# 解包操作
name, age, gender = get_user_info()
性能对比:
python复制import sys
from timeit import timeit
lst = [1, 2, 3]
tup = (1, 2, 3)
print(sys.getsizeof(lst)) # 通常比元组大
print(sys.getsizeof(tup))
# 创建速度测试
print(timeit('x=(1,2,3,4,5)', number=1000000))
print(timeit('x=[1,2,3,4,5]', number=1000000))
1.1.3 字典(Dict) - 高效的键值对存储
字典的核心特性:
- 基于哈希表实现,查找速度快
- 键必须可哈希(不可变类型)
- Python 3.7+保持插入顺序
高级用法:
python复制# 字典推导式
square_dict = {x: x*x for x in range(5)}
# 默认值处理
from collections import defaultdict
word_count = defaultdict(int)
# 合并字典(Python 3.5+)
dict1 = {'a': 1}
dict2 = {'b': 2}
merged = {**dict1, **dict2}
# 视图对象(动态更新)
d = {'a': 1, 'b': 2}
keys = d.keys()
d['c'] = 3
print(keys) # 包含'c'
性能陷阱:
- 避免在循环中频繁创建新字典
- 大字典查找比列表快,但内存占用更高
- 键的哈希冲突会影响性能
1.1.4 集合(Set) - 去重与数学运算
集合的核心特点:
- 无序不重复元素集
- 支持集合运算(并、交、差)
- 基于哈希表实现,查找速度快
实战应用:
python复制# 去重
words = ["hello", "world", "hello"]
unique_words = set(words)
# 集合运算
valid_users = {"user1", "user2", "user3"}
active_users = {"user1", "user3", "user4"}
# 并集
all_users = valid_users | active_users
# 交集
common_users = valid_users & active_users
# 差集
inactive_users = valid_users - active_users
# 快速成员检测
if "user1" in valid_users: # O(1)时间复杂度
print("Valid user")
1.2 文件操作实战详解
1.2.1 基础文件操作模式对比
| 模式 | 描述 | 文件存在 | 文件不存在 | 指针位置 | 可读 | 可写 |
|---|---|---|---|---|---|---|
| r | 只读 | 打开 | 报错 | 开头 | 是 | 否 |
| w | 只写 | 清空 | 创建 | 开头 | 否 | 是 |
| a | 追加 | 打开 | 创建 | 末尾 | 否 | 是 |
| r+ | 读写 | 打开 | 报错 | 开头 | 是 | 是 |
| w+ | 读写 | 清空 | 创建 | 开头 | 是 | 是 |
| a+ | 读写 | 打开 | 创建 | 末尾 | 是 | 是 |
关键点:
- 使用
with语句确保文件正确关闭 - 文本模式要指定编码(推荐utf-8)
- 二进制模式使用
'b'(如图片处理)
1.2.2 文件指针精讲
文件指针控制着读写位置,理解指针行为是掌握文件操作的关键:
python复制# 演示指针行为
with open("demo.txt", "w+") as f:
f.write("Hello World\nPython")
print(f.tell()) # 获取当前指针位置
f.seek(0) # 移动到开头
print(f.read(5)) # 读取5个字符
print(f.tell()) # 指针现在在位置5
f.seek(0, 2) # 移动到文件末尾
f.write("\nEnd")
指针移动方法:
seek(offset, whence=0):whence=0:从文件开头(默认)whence=1:从当前位置whence=2:从文件末尾
tell():返回当前指针位置
1.2.3 大文件处理技巧
处理大文件时,内存效率至关重要:
python复制# 逐行读取(内存友好)
with open("large_file.txt") as f:
for line in f: # 文件对象是可迭代的
process(line)
# 指定缓冲区大小
with open("large_file.txt", "rb", buffering=1024*1024) as f:
while chunk := f.read(4096): # 每次读取4KB
process(chunk)
# 使用生成器处理
def read_in_chunks(file_object, chunk_size=1024):
while True:
data = file_object.read(chunk_size)
if not data:
break
yield data
1.3 JSON文件处理实战
1.3.1 JSON与Python类型对照表
| JSON类型 | Python类型 |
|---|---|
| object | dict |
| array | list |
| string | str |
| number | int/float |
| true | True |
| false | False |
| null | None |
1.3.2 高级JSON操作
自定义编码解码:
python复制import json
from datetime import datetime
class CustomEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime):
return obj.isoformat()
return super().default(obj)
data = {"time": datetime.now()}
json_str = json.dumps(data, cls=CustomEncoder)
JSONPath查询:
python复制# 安装:pip install jsonpath-ng
from jsonpath_ng import parse
data = {
"users": [
{"name": "Alice", "age": 25},
{"name": "Bob", "age": 30}
]
}
expr = parse("$.users[?(@.age > 28)].name")
matches = [match.value for match in expr.find(data)]
print(matches) # ['Bob']
性能优化技巧:
- 对于大JSON文件,使用
ijson库进行流式解析 - 频繁操作时,考虑将JSON转换为Python对象处理
- 使用
orjson替代标准库提升性能(需安装)
2. Pytest测试框架深度解析
2.1 Pytest核心架构
2.1.1 项目目录结构规范
code复制project_root/
├── pytest.ini # 配置文件
├── conftest.py # 全局fixture
├── requirements.txt # 依赖文件
├── src/ # 源代码
├── tests/ # 测试代码
│ ├── unit/ # 单元测试
│ ├── integration/ # 集成测试
│ └── functional/ # 功能测试
├── fixtures/ # 测试数据
├── reports/ # 测试报告
└── docs/ # 文档
2.1.2 pytest.ini配置详解
ini复制[pytest]
# 命令行参数
addopts = -v --color=yes --tb=short -p no:warnings
# 测试发现
testpaths = tests
python_files = test_*.py
python_classes = Test*
python_functions = test_*
# 标记注册
markers =
slow: marks tests as slow (deselect with '-m "not slow"')
integration: integration tests
smoke: smoke test suite
# 日志配置
log_cli = true
log_level = INFO
log_format = %(asctime)s [%(levelname)s] %(message)s
log_date_format = %Y-%m-%d %H:%M:%S
2.2 测试用例设计模式
2.2.1 参数化测试进阶
python复制import pytest
from datetime import datetime, timedelta
# 基本参数化
@pytest.mark.parametrize("input,expected", [
("3+5", 8),
("2+4", 6),
("6*9", 42, marks=pytest.mark.xfail),
])
def test_eval(input, expected):
assert eval(input) == expected
# 动态参数化
def generate_test_data():
return [
(datetime(2023, 1, 1), timedelta(days=1), datetime(2023, 1, 2)),
(datetime(2023, 2, 28), timedelta(days=1), datetime(2023, 3, 1)),
]
@pytest.mark.parametrize("start,delta,expected", generate_test_data())
def test_date_add(start, delta, expected):
assert start + delta == expected
# 参数化组合
@pytest.mark.parametrize("x", [0, 1])
@pytest.mark.parametrize("y", [2, 3])
def test_combinations(x, y):
assert x + y == y + x
2.2.2 Fixture高级用法
python复制import pytest
import tempfile
import os
# 带参数的fixture
@pytest.fixture(params=["utf-8", "utf-16", "ascii"])
def encoding(request):
return request.param
def test_encoding(encoding):
print(f"Testing with encoding: {encoding}")
# 自动使用fixture
@pytest.fixture(autouse=True)
def setup_teardown():
print("\nSetup")
yield
print("\nTeardown")
# 临时目录fixture
@pytest.fixture
def temp_dir():
with tempfile.TemporaryDirectory() as tmpdir:
yield tmpdir
def test_file_operations(temp_dir):
test_file = os.path.join(temp_dir, "test.txt")
with open(test_file, "w") as f:
f.write("hello")
assert os.path.exists(test_file)
2.3 测试报告与持续集成
2.3.1 多格式报告生成
bash复制# 生成HTML报告
pytest --html=report.html --self-contained-html
# 生成JUnit XML报告(Jenkins兼容)
pytest --junitxml=report.xml
# 生成Allure报告
pytest --alluredir=allure-results
allure serve allure-results
# 多报告组合
pytest --html=report.html --junitxml=report.xml --alluredir=allure-results
2.3.2 集成到CI/CD流程
GitLab CI示例:
yaml复制stages:
- test
pytest:
stage: test
image: python:3.9
before_script:
- pip install -r requirements.txt
script:
- pytest --junitxml=report.xml
artifacts:
when: always
paths:
- report.xml
reports:
junit: report.xml
GitHub Actions示例:
yaml复制name: Python 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 -r requirements.txt
- name: Test with pytest
run: |
pytest --cov=./ --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v1
2.4 测试覆盖率分析
bash复制# 安装覆盖率工具
pip install pytest-cov
# 基本覆盖率检查
pytest --cov=myproject tests/
# 生成HTML报告
pytest --cov=myproject --cov-report=html
# 覆盖率阈值设置
pytest --cov=myproject --cov-fail-under=90
在pytest.ini中配置覆盖率:
ini复制[pytest]
addopts = --cov=myproject --cov-report=term-missing
[pytest-cov]
fail_under = 80
3. 接口自动化测试实战
3.1 测试框架设计
3.1.1 分层架构设计
code复制api_framework/
├── config/ # 配置管理
│ ├── __init__.py
│ ├── settings.py # 环境配置
│ └── endpoints.py # API端点配置
├── core/ # 核心功能
│ ├── client.py # API客户端
│ ├── exceptions.py # 自定义异常
│ └── utils.py # 工具函数
├── models/ # 数据模型
│ ├── request.py # 请求模型
│ └── response.py # 响应模型
├── tests/ # 测试用例
│ ├── conftest.py # 测试配置
│ ├── test_login.py # 登录测试
│ └── test_user.py # 用户测试
├── data/ # 测试数据
│ ├── users.json
│ └── products.json
└── reports/ # 测试报告
3.1.2 API客户端实现
python复制import requests
from typing import Optional, Dict, Any
from .exceptions import APIError
class APIClient:
def __init__(self, base_url: str, timeout: int = 30):
self.session = requests.Session()
self.base_url = base_url
self.timeout = timeout
self.headers = {
"Content-Type": "application/json",
"User-Agent": "APITestClient/1.0"
}
def _request(
self,
method: str,
endpoint: str,
params: Optional[Dict] = None,
json: Optional[Dict] = None,
headers: Optional[Dict] = None
) -> Dict[str, Any]:
url = f"{self.base_url}{endpoint}"
req_headers = {**self.headers, **(headers or {})}
try:
response = self.session.request(
method=method,
url=url,
params=params,
json=json,
headers=req_headers,
timeout=self.timeout
)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
raise APIError(f"API request failed: {str(e)}") from e
def get(self, endpoint: str, params: Optional[Dict] = None, headers: Optional[Dict] = None):
return self._request("GET", endpoint, params=params, headers=headers)
def post(self, endpoint: str, json: Optional[Dict] = None, headers: Optional[Dict] = None):
return self._request("POST", endpoint, json=json, headers=headers)
3.2 登录接口测试实战
3.2.1 测试用例设计
python复制import pytest
from core.client import APIClient
from models.response import LoginResponse
@pytest.mark.usefixtures("api_client")
class TestLoginAPI:
@pytest.mark.parametrize("username,password,expected_code", [
("admin", "correct_password", 200),
("admin", "wrong_password", 401),
("nonexistent", "any_password", 404),
("", "", 400),
])
def test_login_status_code(self, api_client, username, password, expected_code):
response = api_client.post(
"/login",
json={"username": username, "password": password}
)
assert response.status_code == expected_code
def test_successful_login(self, api_client):
response = api_client.post(
"/login",
json={"username": "admin", "password": "correct_password"}
)
login_data = LoginResponse(**response.json())
assert login_data.token is not None
assert isinstance(login_data.token, str)
assert len(login_data.token) == 64
assert login_data.user.id == 1
assert login_data.user.role == "admin"
@pytest.mark.performance
def test_login_performance(self, api_client):
import time
start_time = time.time()
for _ in range(100):
api_client.post(
"/login",
json={"username": "admin", "password": "correct_password"}
)
elapsed = time.time() - start_time
assert elapsed < 5.0 # 100 requests should complete in under 5 seconds
3.2.2 测试数据管理
YAML测试数据示例:
yaml复制# test_data/login_cases.yaml
success_cases:
- name: "admin login"
username: "admin"
password: "correct_password"
expected:
status: 200
token_present: true
user_role: "admin"
failure_cases:
- name: "wrong password"
username: "admin"
password: "wrong_password"
expected:
status: 401
error_message: "Invalid credentials"
- name: "empty credentials"
username: ""
password: ""
expected:
status: 400
error_message: "Username and password required"
数据驱动测试实现:
python复制import yaml
import pytest
def load_test_cases(file_path):
with open(file_path) as f:
data = yaml.safe_load(f)
cases = []
# 成功用例
for case in data["success_cases"]:
case["expected"]["should_succeed"] = True
cases.append(pytest.param(
case["username"],
case["password"],
case["expected"],
id=case["name"]
))
# 失败用例
for case in data["failure_cases"]:
case["expected"]["should_succeed"] = False
cases.append(pytest.param(
case["username"],
case["password"],
case["expected"],
id=case["name"]
))
return cases
@pytest.mark.parametrize(
"username,password,expected",
load_test_cases("test_data/login_cases.yaml")
)
def test_login_with_data(api_client, username, password, expected):
response = api_client.post(
"/login",
json={"username": username, "password": password}
)
assert response.status_code == expected["status"]
if expected["should_succeed"]:
data = response.json()
assert "token" in data
assert data["user"]["role"] == expected["user_role"]
else:
assert "error" in response.json()
assert expected["error_message"] in response.json()["error"]
3.3 接口测试最佳实践
3.3.1 测试金字塔实践
-
单元测试:测试单个函数/方法
- 占比:70%
- 特点:快速、隔离、mock外部依赖
-
集成测试:测试模块间交互
- 占比:20%
- 特点:验证组件集成、数据库/缓存交互
-
端到端测试:测试完整流程
- 占比:10%
- 特点:模拟用户行为、覆盖关键路径
3.3.2 测试隔离与清理
python复制import pytest
from models import User, db
@pytest.fixture
def test_user():
user = User.create(username="testuser", password="testpass")
yield user
user.delete() # 测试后清理
@pytest.fixture(scope="module")
def test_db():
db.connect()
db.create_tables([User])
yield
db.drop_tables([User]) # 模块测试后清理所有数据
db.close()
3.3.3 测试报告与告警
自定义报告插件示例:
python复制import pytest
from datetime import datetime
from typing import Dict, List
class TestResult:
def __init__(self):
self.start_time = None
self.end_time = None
self.results: List[Dict] = []
@pytest.fixture(scope="session")
def test_tracker(request):
tracker = TestResult()
tracker.start_time = datetime.now()
def fin():
tracker.end_time = datetime.now()
# 这里可以添加发送报告的逻辑
print(f"\n测试执行时间: {tracker.end_time - tracker.start_time}")
print(f"总用例数: {len(tracker.results)}")
request.addfinalizer(fin)
return tracker
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
report = outcome.get_result()
if report.when == "call" and hasattr(item, "funcargs"):
tracker = item.funcargs.get("test_tracker")
if tracker:
tracker.results.append({
"name": item.name,
"outcome": report.outcome,
"duration": report.duration,
"error": str(report.longrepr) if report.failed else None
})
4. 测试框架高级特性
4.1 自定义标记与筛选
python复制import pytest
import time
# 定义自定义标记
def pytest_configure(config):
config.addinivalue_line(
"markers",
"slow: mark test as slow to run"
)
config.addinivalue_line(
"markers",
"auth: authentication related tests"
)
@pytest.mark.slow
def test_complex_calculation():
time.sleep(2)
assert 1 + 1 == 2
@pytest.mark.auth
class TestAuthentication:
def test_login(self):
assert True
@pytest.mark.parametrize("user_type", ["admin", "user", "guest"])
def test_permissions(self, user_type):
assert user_type in ["admin", "user", "guest"]
# 运行指定标记的测试
# pytest -m "auth and not slow"
4.2 插件开发实战
自定义插件示例:
python复制# conftest.py
import pytest
from _pytest.runner import TestReport
def pytest_addoption(parser):
parser.addoption(
"--env",
action="store",
default="staging",
help="Environment to run tests against"
)
parser.addoption(
"--record",
action="store_true",
default=False,
help="Record test results to database"
)
@pytest.fixture(scope="session")
def env(request):
return request.config.getoption("--env")
@pytest.fixture(autouse=True)
def record_test(request, env):
if not request.config.getoption("--record"):
yield
return
start_time = time.time()
yield
duration = time.time() - start_time
test_name = request.node.name
test_outcome = "passed" if request.node.rep_call.passed else "failed"
# 这里可以添加记录到数据库的逻辑
print(f"Recorded test: {test_name} ({test_outcome}) in {duration:.2f}s")
@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
rep = outcome.get_result()
setattr(item, "rep_" + rep.when, rep)
4.3 分布式测试执行
bash复制# 安装pytest-xdist
pip install pytest-xdist
# 并行运行测试(4个worker)
pytest -n 4
# 按负载均衡模式运行
pytest -n auto
# 指定分发算法
pytest -n 4 --dist=loadscope # 按测试类分发
pytest -n 4 --dist=loadfile # 按测试文件分发
分布式测试注意事项:
- 确保测试用例相互独立
- 共享资源需要特殊处理(如测试数据库)
- 避免在测试中修改全局状态
- 使用
pytest-xdist的--looponfail选项实现快速反馈
4.4 测试覆盖率高级用法
多模块覆盖率报告:
bash复制# 分别测量不同模块的覆盖率
pytest --cov=module1 --cov=module2 --cov-report=html
# 设置覆盖率精度
pytest --cov=src --cov-branch --cov-report=term-missing:skip-covered
# 生成LCov格式报告
pytest --cov=src --cov-report=lcov
在代码中排除特定部分:
python复制def uncovered_function(): # pragma: no cover
pass
class TestClass:
def test_method(self):
... # 正常测试代码
def utility_method(self): # pragma: no cover
... # 测试辅助方法不需要覆盖
5. 测试框架集成与扩展
5.1 与Docker集成
测试环境Docker化:
dockerfile复制# test.Dockerfile
FROM python:3.9
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["pytest", "-v", "--cov=.", "--cov-report=xml"]
使用Docker Compose管理依赖:
yaml复制# docker-compose.test.yml
version: '3'
services:
tests:
build:
context: .
dockerfile: test.Dockerfile
depends_on:
- db
- redis
environment:
DB_HOST: db
REDIS_HOST: redis
db:
image: postgres:13
environment:
POSTGRES_PASSWORD: testpass
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:6
volumes:
postgres_data:
5.2 与Mock框架集成
使用pytest-mock进行测试隔离:
python复制import pytest
from unittest.mock import Mock, call
def test_mocking(mocker):
# 模拟函数
mock_func = mocker.patch("module.function")
mock_func.return_value = 42
assert module.function() == 42
mock_func.assert_called_once()
# 模拟类方法
mock_method = mocker.patch.object(SomeClass, "method")
mock_method.side_effect = [1, 2, 3]
obj = SomeClass()
assert obj.method() == 1
assert obj.method() == 2
assert obj.method() == 3
assert mock_method.call_count == 3
# 模拟上下文管理器
mock_open = mocker.patch("builtins.open", mocker.mock_open(read_data="data"))
with open("file.txt") as f:
assert f.read() == "data"
mock_open.assert_called_with("file.txt")
5.3 性能测试集成
使用pytest-benchmark进行性能测试:
python复制import pytest
@pytest.mark.benchmark
def test_list_creation(benchmark):
def create_list(n):
return [i for i in range(n)]
result = benchmark(create_list, 1000)
assert len(result) == 1000
@pytest.mark.benchmark
def test_dict_merge(benchmark):
def merge_dicts(d1, d2):
return {**d1, **d2}
d1 = {str(i): i for i in range(100)}
d2 = {str(i): i*2 for i in range(50, 150)}
result = benchmark(merge_dicts, d1, d2)
assert len(result) == 150
基准测试结果分析:
bash复制pytest --benchmark-autosave
pytest --benchmark-compare=0001 --benchmark-compare-fail=min:5% --benchmark-columns=min,max,mean,stddev,rounds
5.4 安全测试集成
使用pytest插件进行安全测试:
python复制import pytest
import requests
@pytest.mark.security
class TestSecurity:
@pytest.mark.parametrize("endpoint", [
"/login",
"/user/profile",
"/admin/dashboard"
])
def test_https_required(self, endpoint, api_client):
response = requests.get(
api_client.base_url.replace("https", "http") + endpoint,
allow_redirects=False
)
assert response.status_code in (301, 302, 403)
def test_sql_injection(self, api_client):
payload = {
"username": "admin' --",
"password": "anything"
}
response = api_client.post("/login", json=payload)
assert response.status_code != 500
assert "SQL" not in response.text
def test_xss_protection(self, api_client):
payload = {
"comment": "<script>alert('XSS')</script>"
}
response = api_client.post("/comments", json=payload)
assert "<script>" not in response.text
6. 测试框架维护与优化
6.1 测试代码重构技巧
消除重复代码:
python复制# 重构前
def test_add_user():
response = client.post("/users", json={
"name": "Alice",
"email": "alice@example.com",
"password": "secure123"
})
assert response.status_code == 201
assert response.json()["name"] == "Alice"
def test_add_admin():
response = client.post("/users", json={
"name": "Admin",
"email": "admin@example.com",
"password": "admin123",
"role": "admin"
})
assert response.status_code == 201
assert response.json()["role"] == "admin"
# 重构后
@pytest.mark.parametrize("user_data,expected", [
(
{"name": "Alice", "email": "alice@example.com", "password": "secure123"},
{"status": 201, "name": "Alice", "role": None}
),
(
{"name": "Admin", "email": "admin@example.com", "password": "admin123", "role": "admin"},
{"status": 201, "name": "Admin", "role": "admin"}
)
])
def test_add_user(user_data, expected):
response = client.post("/users", json=user_data)
assert response.status_code == expected["status"]
data = response.json()
assert data["name"] == expected["name"]
if expected["role"] is not None:
assert data["role"] == expected["role"]
6.2 测试执行优化策略
测试选择与排序:
bash复制# 只运行上次失败的测试
pytest --lf
# 先运行上次失败的测试
pytest --ff
# 按测试持续时间排序(从快到慢)
pytest --durations=0
# 只运行修改后影响的测试(需要pytest-picked)
pytest --picked
测试并行化配置:
ini复制# pytest.ini
[pytest]
addopts = -n auto
xfail_strict = true
6.3 测试数据管理策略
使用工厂模式创建测试数据:
python复制# tests/factories.py
import factory
from models import User, Post
class UserFactory(factory.Factory):
class Meta:
model = User
username = factory.Sequence(lambda n: f"user{n}")
email = factory.LazyAttribute(lambda obj: f"{obj.username}@example.com")
is_active = True
class PostFactory(factory.Factory):
class Meta:
model = Post
title = factory.Faker("sentence")
content = factory.Faker("paragraph")
author = factory.SubFactory(UserFactory)
# 在测试中使用
def test_post_creation():
post = PostFactory(title="Test Post")
assert post.title == "Test Post"
assert post.author.is_active is True
6.4 测试监控与告警
集成Prometheus监控:
python复制# conftest.py
from prometheus_client import start_http_server, Counter, Gauge
TEST_REQUESTS = Counter(
"test_requests_total",
"Total test API requests",
["method", "endpoint", "status"]
)
TEST_DURATION = Gauge(
"test_duration_seconds",
"Test execution duration",
["test_name"]
)
def pytest_sessionstart(session):
start_http_server(8000)
@pytest.fixture(autouse=True)
def track_test_metrics(request):
start_time = time.time()
yield
duration = time.time() - start_time