25 分钟
工程化

测试体系深度

pytest、Mock、测试覆盖率、属性测试和测试金字塔

  • 掌握 pytest 的 fixture/parametrize/mark
  • 理解 Mock 的正确用法和反模式
  • 掌握测试覆盖率
  • 了解属性测试(hypothesis)和测试金字塔

测试体系深度

没有测试的代码是遗留代码。工业级项目的测试不是随便写几个 assert,而是一套完整的质量保障体系。

pytest 核心

Python
# pytest 基础
# 文件:test_*.py 或 *_test.py
# 函数:test_*

import pytest

# 1. 基本断言(pytest 重写 assert,提供详细输出)
def test_add():
    assert 1 + 1 == 2
    assert "hello".upper() == "HELLO"
    assert [1, 2, 3] == [1, 2, 3]

# 2. 参数化测试
@pytest.mark.parametrize("a,b,expected", [
    (1, 2, 3),
    (0, 0, 0),
    (-1, 1, 0),
    (100, 200, 300),
])
def test_add_param(a, b, expected):
    assert a + b == expected

# 3. fixture(测试的依赖注入)
@pytest.fixture
def sample_list():
    return [1, 2, 3]

def test_list(sample_list):
    assert len(sample_list) == 3

# 4. fixture 作用域
@pytest.fixture(scope="module")  # function/class/module/session
def db_connection():
    conn = create_connection()
    yield conn  # yield 之前是 setup,之后是 teardown
    conn.close()
# 上面是标准 pytest 测试文件的写法:在专业开发环境里用 pytest 命令自动发现并运行;
# App 内置的是教学运行环境(未捆绑 pytest 命令行),所以这一块只作语法展示,
# 紧接着下面用不依赖任何第三方库的纯 Python,帮你亲手体会「参数化」和「fixture」到底在做什么。
示例代码(可运行)

unittest.mock — Mock 的正确用法

示例代码(可运行)
🐍资深工程师经验谈

Mock 的黄金法则:mock 外部边界,不 mock 内部逻辑。好的测试应该是:Arrange→Act→Assert。一个测试只测一个行为。测试名应该是句子:test_转账时余额不足应该抛出异常。如果测试需要 10 个 mock,说明你的代码耦合太高了——该重构了。

测试覆盖率

Python
# coverage.py / pytest-cov
# pip install pytest-cov
# pytest --cov=mypackage --cov-report=html
# pytest --cov=mypackage --cov-report=term-missing
# 配置在 pyproject.toml [tool.coverage]
# branch = true, fail_under = 80
# 覆盖率目标:核心逻辑90%+, 整体80%+
print("覆盖率不是目的,测试质量才是")

测试金字塔

Python
# 测试金字塔
# 顶层: E2E(少量,慢,真实环境)
# 中层: 集成测试(适量,验证模块协作)
# 底层: 单元测试(大量,快,隔离)
# 反模式: 冰淇淋(太多E2E,太慢不稳定)
# 正确: 大量单元测试 + 适量集成测试 + 少量E2E
print("单元测试 > 集成测试 > E2E测试")

# 测试类型:
# - 单元测试:隔离测试单个函数/类,mock外部依赖,毫秒级
# - 集成测试:测试多个模块协作,用真实数据库(测试容器)
# - E2E测试:从用户视角测试完整流程,浏览器/API
# - 契约测试:验证服务间API契约(pact)
# - 性能测试:pytest-benchmark / locust
# - 突变测试:mutmut(验证测试能否捕获代码变异)
选学

属性测试 (Property-Based Testing)

普通测试要写具体的输入和期望输出。属性测试描述「性质」,让框架自动生成大量测试用例。

Python
# pip install hypothesis
from hypothesis import given, strategies as st

# 性质:排序后再排序结果不变
def test_sort_idempotent():
    # 但用 hypothesis 自动生成各种列表
    pass

# @given(st.lists(st.integers()))
# def test_sort_twice(lst):
#     assert sorted(sorted(lst)) == sorted(lst)

# @given(st.text())
# def test_reverse_reverse(s):
#     assert s[::-1][::-1] == s

# hypothesis 会自动生成边界情况:
# 空列表、超长列表、负数、Unicode、NaN...
# 找到失败用例后自动缩小到最小反例
选择题

fixture 的 scope='module' 意味着什么?

选择题

Mock 的黄金法则是?

资深工程师加餐

底层原理 · 大厂视角 · 工程经验,点卡片展开

底层是大量、快速、稳定的单元测试(优先覆盖纯函数与边界/异常路径),中间是少量集成测试(验证模块协作),顶端是少量端到端测试(慢且脆)。测试最大的价值是当「安全网」:有它你才敢放心重构。没测试兜底的重构,本质是在赌博。写用例先想正常、边界、异常三类。

挑战任务

pytest 挑战

简单+50 XP

就用 pytest 风格写参数化测试,测字符串反转函数。

pytest 挑战
2 个测试用例

课后作业

计算器测试

中等+20 XP

给 divide 函数写完整 pytest 测试用例,用 parametrize 覆盖正常、边界和异常情况。

计算器测试
1 个测试用例