Python 用 pytest 做测试
pytest 用断言即测试、fixture 与参数化组织可维护测试套件。
#type / concept
#status / growing
#tech / dev
#resource / python
#tech / lang / python
[!info] 关联笔记
Python 用 pytest 做测试
这个概念为什么出现
没有自动化测试,重构与依赖升级都在赌。pytest 以低样板成为 Python 事实标准:普通 assert、丰富插件、fixture 注入。
[!abstract] 一句话理解 pytest 收集
test_*,用 assert 验证行为;fixture 管理依赖,参数化覆盖多例。
最小可运行示例
先把示例放进业务场景,再看代码:
场景:为折扣函数写参数化测试
# test_discount_pytest_style.py
# 业务意图:验证折扣计算(可直接 pytest 本文件)。
# 教学点:
# - assert;
# - pytest.mark.parametrize;
# - 异常断言。
import pytest
def price_after_discount(price_cents: int, discount: float) -> int:
if not 0 <= discount <= 1:
raise ValueError("bad discount")
return int(price_cents * (1 - discount))
@pytest.mark.parametrize(
"price,discount,expected",
[(1000, 0.1, 900), (1000, 0.0, 1000)],
)
def test_price_after_discount(price, discount, expected):
assert price_after_discount(price, discount) == expected
def test_bad_discount():
with pytest.raises(ValueError):
price_after_discount(1000, 2)
建议运行:
pytest -q test_discount_pytest_style.py
期望输出:
...
3 passed
(若环境未装 pytest:pip install pytest 后重跑。)
结合场景再看三个关注点
- 表驱动减少重复测试函数。
- raises 锁定失败契约。
- 业务函数与测试可同逻辑演进。
Fixture、Fake 与 monkeypatch 怎么配合
这三个词经常一起出现,但职责不同。
[!abstract] 先理解设计,再记 API
- fixture:准备测试需要的对象或环境
- Fake:用一个简化实现替代昂贵、不稳定或不想一起测试的真实依赖
- monkeypatch:在测试期间临时把真实对象替换成 Fake / Stub
例如只想测试 FastAPI route,而不想真的运行 DiagnosisService 和 LLM:
测试目标
↓
FastAPI Route
↓
Fake DiagnosisService
真实 DiagnosisService / LLM 不进入这条测试路径
pytest 可以这样完成替换:
class FakeDiagnosisService:
async def generate_diagnosis(self, **kwargs):
return {"diagnoses": []}
def test_route(client, monkeypatch):
fake = FakeDiagnosisService()
monkeypatch.setattr(
"src.api.routes.diagnosis.get_diagnosis_service",
lambda: fake,
)
response = client.post(
"/api/diagnosis/analyze",
json={"extracted_info": [], "profile": {}},
)
assert response.status_code == 200
这里真正重要的不是 monkeypatch.setattr 语法,而是测试边界:
我只验证 Route 的 HTTP 行为
↓
所以把更深层的真实依赖隔离掉
monkeypatch 是工具,Fake 是测试替身;它们都不是 Unit / Integration / E2E 之外的新测试层级。完整模型见 测试层级与测试边界。
核心概念与准确模型
- 发现规则:
test_*.py/Test*/test_* - fixture 作用域:function/module/session
- conftest.py 共享
- 插件:cov、xdist、mock
边界情况与反直觉行为
- 断言重写让失败信息更详,但奇妙魔法需了解。
- 测试间污染全局状态。
- 异步测试需插件/模式。
常见误区
[!warning] 常见误区:只测成功路径 错误理解:happy path 足够。
正确模型:边界与异常同样是契约。
工程实践
- Arrange-Act-Assert
- 命名表达场景
- CI 默认
pytest
本节总结
pytest 让测试成为日常反馈环。先测行为,再谈覆盖率数字。
自测题
- fixture 解决什么?
- 参数化适合什么?
- Fake 和 monkeypatch 的职责有什么不同?
参考答案
- 可复用的测试依赖准备/清理。
- 同一逻辑多组输入输出。
- Fake 是替代真实依赖的简化实现;monkeypatch 是测试期间完成临时替换的工具。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| pytest docs | 文档 | 官方 |