应使用 coverage run --source=myapp,myproject manage.py test 命令,并配置 .coveragerc 限定统计范围、排除测试文件和脚本,避免覆盖率失真;missing 行多因异步、信号、模板或事务未实际执行所致。

怎么用 coverage.py 测 Django 项目的测试覆盖率
直接跑 coverage run 不行——Django 的测试命令(python manage.py test)会接管执行流程,跳过 coverage 的钩子。得让 coverage 去“包裹”整个 Django 测试过程,而不是反过来。
- 在项目根目录下运行:
coverage run --source=myapp,myproject manage.py test(把myapp和myproject换成你实际的 app 名和项目包名) -
--source很关键:它限定只统计这些模块里的代码,否则 coverage 会把 Django 自身、venv 里所有依赖都扫进去,覆盖率数字失真 - 别用
coverage run -m pytest或类似方式混搭——Django 的 TestCase 类依赖manage.py初始化数据库、配置等,绕过它大概率报django.core.exceptions.ImproperlyConfigured: Requested setting DATABASES, but settings are not configured
覆盖率报告里为什么显示大量 manage.py 或 __init__.py 文件
这是没设好 --source 或漏了 .coveragerc 配置导致的典型噪音。coverage 默认扫描当前目录下所有 .py 文件,包括脚本和空初始化文件。
- 创建
.coveragerc放在项目根目录,内容至少包含:[run] source = myapp omit = */tests/*,*/test_*.py,manage.py,__init__.py
-
omit里写的是 glob 模式,不是正则;*/tests/*能排除掉myapp/tests/下所有文件,但不会误杀myapp/test_utils.py - 如果用了 pytest-django,注意它默认把
conftest.py当测试文件处理,也要加进omit
为什么 coverage report -m 显示某行标为 “missing”,但明明写了测试
常见于数据库操作、信号、中间件或异步任务——coverage 只能跟踪同步执行流,对被延迟触发、跨进程或未实际进入的分支无能为力。
Python 3.14.2是Python编程语言在2025年12月5日发布的稳定版本,属于3.14系列的第二个维护更新。该版本包含了18项修复,重点解决了多进程、数据类及正则表达式等模块的回归问题,并修复了CVE-2025-12084等安全漏洞。此版本标志着自由线程模式(移除GIL)正式获得官方支持,是Python发展的重要里程碑。
- Django 的
post_save信号:测试里调用了instance.save(),但信号函数没被执行?检查是否在测试设置里禁用了信号(SIGNALS配置或手动 disconnect),或者信号注册发生在非apps.py的冷加载路径 - 模板中的
{% if user.is_authenticated %}这类逻辑:模板渲染不经过 Python 行级执行,coverage 看不到,得靠集成测试+截图或断言响应内容来覆盖 - 使用
transaction.atomic()包裹的代码块:如果测试里没显式触发回滚(比如故意抛异常),里面的代码可能根本没跑,coverage 就标 missing
CI 环境下覆盖率阈值失败但本地正常
最常踩的坑是数据库后端差异:本地用 SQLite,CI 用 PostgreSQL,而某些 QuerySet 方法(如 .distinct('field'))在 SQLite 不报错,在 PG 上会抛 NotSupportedError,导致测试提前退出,对应代码段完全没执行。
- 确保 CI 的
settings.py和本地测试环境一致,尤其是DATABASES和DEBUG(DEBUG=True有时会影响 query 日志和 ORM 行为) - CI 中避免用
python manage.py test --keepdb:缓存数据库状态会让部分测试跳过 setup,coverage 统计不全 - 运行前加一句
coverage debug sys,确认 CI 环境里 coverage 真的加载了你的.coveragerc,而不是用了默认配置
覆盖率数字本身不重要,重要的是 missing 行背后有没有被忽略的执行路径。尤其当某段 error handling 逻辑一直标 missing,大概率是测试没真正触发那个异常分支——这时候补一个 with self.assertRaises(...) 比调高阈值实在得多。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










