用coverage.py统计Django测试覆盖率需安装配置后包裹执行,优先选原生TestCase应对Celery等复杂场景,聚焦未覆盖分支与信号处理等易漏点。

用 coverage.py 统计 Django 测试覆盖率
直接跑 manage.py test 不会输出覆盖率,必须用 coverage 包裹执行。它不是 Django 内置功能,得单独装、单独配。
- 安装:
pip install coverage - 配置文件
.coveragerc放项目根目录,关键字段不能漏:[run] source = . omit = */migrations/* */tests/* */settings/* manage.py */wsgi.py */asgi.py [report] show_missing = true skip_empty = true
-
source = .表示从当前目录开始扫描 Python 文件;omit里漏掉迁移、测试、配置等非业务代码,否则覆盖率会被拉低
pytest-django 与原生 unittest 的选择陷阱
别只看语法简洁就选 pytest-django —— 它的 @pytest.mark.django_db 在某些场景下会和 Celery、ASGI 或自定义数据库连接池冲突。比如 worker 进程启动时,pytest fixture 可能抢在 Django 配置加载前初始化测试 DB,导致 django.core.exceptions.AppRegistryNotReady。
- 纯 Web API 项目(无后台任务):用 pytest-django 没问题,写法更轻量
- 含 Celery、Channels 或自定义 DB 连接管理的项目:优先用 Django 原生
TestCase,靠setUpTestData和事务回滚控制速度 - 混合项目:可在
pytest.ini中加--reuse-db和--keepdb,但要手动清空测试 DB 状态,否则后续测试可能读到上一轮残留数据
让覆盖率真正反映风险点
85% 的覆盖率数字没意义,关键是看哪些分支没覆盖。Django 模型里一个 if self.status == 'draft' 判断,如果所有测试都只造 status='published' 数据,那这个分支永远标红。
- 检查报告里的
MISSING列:重点关注views.py中的异常路径、forms.py的 clean 方法、models.py的 save() 重写逻辑 - 避免“假覆盖”:用
self.client.get('/api/user/')跑通不等于覆盖了权限校验。得补self.client.force_login(another_user)和未登录两种 case - 信号(signals)容易漏:比如
post_save触发的异步任务,必须显式调用transaction.on_commit(...)或 mock 掉实际调用,否则覆盖率统计不到
CI 中稳定运行的关键参数
本地跑得通,CI 上失败,大概率是数据库或并发问题。Django 默认为每个测试用例建/删 DB,CI 环境下耗时且易冲突。
- 加
--keepdb:跳过建库步骤,前提是 CI job 保证单个 runner 串行执行 - PostgreSQL 测试库名必须带
test_前缀,且提前在 CI 环境中创建好空库(Django 不会自动建库) - 用
python -m pytest --tb=short -xvs而不是manage.py test:前者错误堆栈更短,CI 日志易读;-x遇错即停,省时间
真实项目里最常被忽略的,是信号处理和事务边界内的状态变更——它们不报错,但覆盖率报告里永远有一块空白区域,直到某次上线后才发现某个 status 转换逻辑根本没被执行过。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











