07.1
测试金字塔
大量快而廉价的单元测试打底,少量集成居中,更少但更慢的端到端封顶。重心放单测,性价比最高。
pytest 是事实标准
相比 unittest,pytest 断言用原生 assert、fixture 更优雅、插件生态丰富(cov、mock、asyncio)。
07.2
pytest 入门
测试文件以 test_ 开头,函数以 test_ 开头,直接 assert 即可,无需断言方法。
# test_cart.py
def test_add_total():
c = Cart()
c.add(Product("A", 10)); c.add(Product("B", 5))
assert c.total == 1507.3
fixture
fixture 提供可复用的测试资源(DB、client),支持作用域与 teardown(yield)。
import pytest
@pytest.fixture
def client():
app = create_app()
with app.test_client() as c:
yield c
def test_home(client):
assert client.get("/").status_code == 20007.4
参数化
@pytest.mark.parametrize 用一组数据驱动同一用例,减少重复。
@pytest.mark.parametrize("a,b,expect", [(1,2,3),(0,0,0),(-1,1,0)])
def test_add(a, b, expect):
assert add(a, b) == expect07.5
mock 隔离
unittest.mock 替换外部依赖(网络/DB),让单测快而稳,并断言调用行为。
from unittest.mock import MagicMock, patch
def test_fetch(monkeypatch):
fake = MagicMock(return_value={"id": 1})
monkeypatch.setattr("mymodule.api", fake)
assert load()["id"] == 107.6
覆盖率
pytest-cov 量化哪些代码被测试覆盖,设定阈值防止回归。
pip install pytest-cov
pytest --cov=mymodule --cov-report=term-missing07.7
异步与 Web 测试
pytest-asyncio 测试协程;FastAPI 提供 TestClient/AsyncClient 做接口测试。
@pytest.mark.asyncio
async def test_async():
assert await fetch(1) == {"id": 1}07.8
常见陷阱
多因触碰真实网络/数据库或启动完整服务。用 mock/fixture 替换外部依赖,把被测单元缩到纯逻辑。
monkeypatch 在用例结束后自动还原,比手动 mock.patch 更安全。注意只 patch 被测模块实际引用的对象路径。
07.9