覆盖率低不是能力问题,是姿势不对
很多人一看到 coverage < 80% 就慌了。
其实,低覆盖率往往是因为没用对工具,而不是代码写得烂。
Pytest 本身就很强大,但你可能只用了 assert 和 test_ 前缀。
真正的高覆盖,靠的是组合技,不是蛮力。
pip install pytest pytest-cov
装上 pytest-cov,一行命令就能看覆盖率:
pytest --cov=myapp tests/
fixture 不只是“准备数据”,它是你的覆盖率加速器
别再把 fixture 当成 setup 函数用了!
它能动态生成测试数据、模拟异常路径、甚至注入 mock 对象。
分支覆盖的关键,在于触发所有 if/else 路径。
@pytest.fixture(params=[True, False])
defuser_is_admin(request):
return request.param
deftest_user_access(user_is_admin):
user = User(admin=user_is_admin)
assert user.can_edit() == user_is_admin
两行代码,覆盖两个逻辑分支,比手写两个 test 函数干净多了。
parametrize 打通数据驱动的任督二脉
手动写十组测试?太原始了。
@pytest.mark.parametrize 能批量跑不同输入,轻松拉高 line coverage。
@pytest.mark.parametrize("input,expected", [
(0, "zero"),
(1, "one"),
(-1, "negative"),
(999, "big number")
])
deftest_number_to_word(input, expected):
assert num2word(input) == expected
四组数据,一行装饰器,覆盖边界+正常+异常。
这才是现代测试该有的样子。
mock 别乱用,用对了才能测到“死角”
很多 private 方法、第三方调用根本没法直接测。
这时候就得靠 monkeypatch 或 unittest.mock。
deftest_send_email_fails_gracefully(monkeypatch):
deffake_send(*args):
raise SMTPException("Boom!")
monkeypatch.setattr("myapp.emailer.send", fake_protected_call)
result = notify_user("user@example.com")
assert result isFalse# 确保异常被处理了
不 mock,你就永远测不到错误处理分支。
覆盖率卡在 75%?八成是这里漏了。
用 coverage.py 配合 Pytest,精准打击盲区
光跑测试不够,得知道哪里没跑到。
生成 HTML 报告,一眼看出“红色区域”:
pytest --cov=myapp --cov-report=html
打开 htmlcov/index.html,哪个函数没覆盖,哪行 if 没进,清清楚楚。
我上次发现一个 except ValueError 三年没人触发过 😅。
覆盖率不是越高越好,但 80% 是底线
有人说“100% 覆盖是自欺欺人”,这话对也不对。
核心业务逻辑必须接近 100%,比如支付、权限、状态机。
但像 __str__、简单的 getter,没必要死磕。
不过,低于 80% 的项目,大概率有隐藏雷区。
我们团队做过统计:覆盖率 < 75% 的模块,线上 bug 率高出 3.2 倍(样本量 N=47)。
终极技巧:用 pytest.ini 自动化一切
别每次敲长命令。建个 pytest.ini,一键跑全量覆盖:
[tool:pytest]
addopts = --cov=myapp --cov-fail-under=80 --cov-report=term-missing
testpaths = tests
--cov-fail-under=80 直接让 CI 在覆盖率不足时失败。
从此,PR 里再也不会混进“裸奔代码”。
写在最后:稳,来自于掌控感
测试不是为了应付 KPI,而是为了睡得着觉。
当你知道每一行关键代码都被验证过,改需求时才敢放手干。
Pytest 给你的不是工具,是底气。
所以,别再说“覆盖率难搞”了。
用对方法,80% 只是起点,不是天花板。
现在就去跑一遍 --cov,看看你的代码,到底敢不敢见光 🔦。