Python 开发手册PYTHON

CHAPTER 07质量保障 · TESTING

测试

没有测试,重构就是赌博。本章覆盖 pytest 用例、fixture 资源管理、mock 隔离、参数化与覆盖率,守住质量底线。

开始阅读难度中级约2小时
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 == 15
07.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 == 200
07.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) == expect
07.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"] == 1
07.6

覆盖率

pytest-cov 量化哪些代码被测试覆盖,设定阈值防止回归。

pip install pytest-cov
pytest --cov=mymodule --cov-report=term-missing
07.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 全局污染?

monkeypatch 在用例结束后自动还原,比手动 mock.patch 更安全。注意只 patch 被测模块实际引用的对象路径。

07.9

实战练习

延伸资源