← 返回题目列表

Python 项目如何在 CI 中做自动化测试?

高频 中等 第 11 / 27 题 更新于 2026/07/27
CI自动化测试GitHub Actions工程化

简化版

Python 项目 CI 通常在每次提交或合并请求时自动安装依赖、运行格式检查、静态检查、单元测试、覆盖率统计和构建验证。CI 的目标是尽早发现问题,防止不合格代码进入主分支。

详细版

典型 CI 流程包括:

  1. 检出代码;
  2. 安装指定 Python 版本;
  3. 安装依赖;
  4. 运行格式检查,如 black、ruff;
  5. 运行类型检查,如 mypy;
  6. 运行 pytest;
  7. 生成覆盖率;
  8. 构建包或镜像;
  9. 上传测试报告或覆盖率报告。

示例命令:

ruff check .
black --check .
mypy app
pytest --cov=app --cov-fail-under=80

面试要强调:CI 不只是跑测试,它是代码质量门禁和团队协作流程的一部分。

完整版教学

一、CI 的核心价值

CI 是 Continuous Integration,持续集成。它的核心价值是让代码尽快合并、尽快验证、尽快暴露问题。

没有 CI 时,开发者可能在本地忘记跑测试,或者本地环境和别人不同,代码合并后才发现失败。CI 把验证流程放到统一环境中执行,让每次提交都有可重复的质量检查。

对于 Python 项目,CI 尤其重要,因为 Python 是动态语言,很多错误只有运行或静态检查时才会暴露,比如导入错误、拼写错误、类型不匹配、依赖缺失。

二、一个健康的 Python CI 应该跑什么

基础 CI 至少应该跑测试:

pytest

更完整的 CI 应该包含格式、静态检查、类型检查和覆盖率:

ruff check .
black --check .
mypy app
pytest --cov=app --cov-report=xml

如果项目是库,还应该构建包:

python -m build

如果项目是服务,还可以构建 Docker 镜像并做最小启动验证。

三、CI 为什么要固定 Python 版本和依赖

本地能跑不代表 CI 能跑,CI 能跑也不代表生产能跑。为了减少环境差异,CI 应明确 Python 版本,并使用锁定的依赖文件。

例如项目使用 Python 3.11,CI 就不要默认使用系统 Python。依赖也应该通过 lock 文件或固定版本安装,避免今天安装和明天安装得到不同依赖。

可以使用 pip-tools、Poetry、uv 等工具生成锁文件。这样 CI 安装依赖更稳定。

四、测试分层在 CI 中怎么安排

并不是所有测试都必须每次提交全量跑。常见策略是:

  • 每次提交跑格式检查、静态检查、单元测试;
  • 合并请求跑关键集成测试;
  • 发布前跑完整集成测试和端到端测试;
  • 定时任务跑慢测试和兼容性测试。

pytest 可以用 marker 分组:

@pytest.mark.slow
def test_large_import():
    ...

CI 中可以选择:

pytest -m "not slow"

这样能在反馈速度和测试完整性之间取得平衡。

五、CI 中如何处理数据库和外部服务

集成测试可能需要 PostgreSQL、Redis、消息队列。CI 中可以用服务容器、Docker Compose 或 Testcontainers 启动依赖。

关键原则是:CI 使用测试环境,不连接生产资源;测试数据可重复创建和清理;外部第三方服务尽量 mock 或使用沙箱环境。

敏感信息要通过 CI Secret 注入,不要写进代码或日志。测试失败时,也不要把 token、密码、连接串打印出来。

六、覆盖率门禁和报告

CI 可以设置覆盖率阈值:

pytest --cov=app --cov-fail-under=80

也可以上传 XML 报告到覆盖率平台。团队更应该关注新增代码覆盖率和核心模块覆盖率,而不是只看项目总覆盖率。

老项目如果历史覆盖率很低,可以先不强行全局高门槛,而是要求新增代码必须有测试,逐步提升质量。

七、常见误区与追问

CI 阶段常见命令失败意义
格式检查black --check .风格未统一
Lintruff check .存在未使用导入、潜在 bug 等
类型检查mypy app类型契约不一致
测试覆盖率pytest --cov=app --cov-fail-under=80行为验证或覆盖率不足
提交代码 -> 安装锁定依赖 -> lint/type/test 并行跑 -> 上传报告 -> 门禁通过后允许合并
如果 CI 需要 20 分钟,团队很可能绕过;控制在几分钟内更健康。

记忆钩子:CI 是主分支门禁,不是“自动跑一下 pytest”的装饰。

第一个误区是 CI 只跑 pytest,不跑 lint 和类型检查。很多问题测试未必覆盖,但静态工具可以提前发现。

第二个误区是 CI 太慢。太慢的 CI 会让团队绕过它。应该缓存依赖、拆分任务、分层运行测试。

第三个误区是 CI 依赖真实外部服务。外部服务抖动会让测试假失败,团队逐渐不信任 CI。

第四个误区是允许失败的 CI 合并主分支。门禁没有强制力,就很难形成质量约束。

  • 误区:CI 只跑 pytest 就完整了。 测试不一定覆盖所有路径,格式、lint、类型检查、构建验证能提前发现另一类问题。
  • 误区:CI 越全越好,不用管速度。 CI 太慢会拖慢反馈甚至被绕过,应通过缓存依赖、拆分任务、分层测试来控制耗时。
  • 误区:CI 可以连真实生产依赖。 CI 必须使用测试库、服务容器、沙箱或 mock,不能污染生产资源,也不能泄露密钥。
  • 追问:为什么要锁定 Python 版本和依赖? CI 的价值是可重复验证,版本漂移会导致今天通过、明天失败或与生产不一致。
  • 追问:慢测试怎么放进 CI? 可以用 pytest marker 分组,每次提交跑快速测试,发布前或定时任务跑慢集成和 E2E。
  • 追问:CI 失败是否允许合并? 主分支门禁通常不应允许失败合并,否则质量约束会失去可信度。

八、加强记忆

记 Python CI:统一环境里自动跑格式、静态检查、类型检查、测试、覆盖率和构建。CI 的价值不是炫流程,而是把质量问题挡在主分支外,同时让团队对每次合并有信心。