我帮客户 3 天搭建 pytest 框架并集成 CI 的实战踩坑实录
上个月接了个金融类客户的内部管理系统优化项目。他们团队大概 5 个人,每次发版都要手工点一遍核心流程,耗时半天。客户希望我能把自动化测试搞起来,目标是把回归时间压到 10 分钟以内。说实话,这种需求我见多了,难点从来不在工具本身,而在怎么让业务同事愿意用。他们之前试过写脚本,但维护成本太高,最后都烂尾了。第一次开会时,测试组长直言不讳地说,只要别增加他们的工作量,什么都行。这句话我记在了心里。项目规模不大,但业务逻辑复杂,涉及资金流转,容错率极低。
框架选型与数据隔离策略
当时有方案 A 用 unittest,方案 B 用 pytest。我选了 pytest,因为 fixture 管理方便,插件生态好。unittest 写起来太啰嗦,setUp 和 tearDown 嵌套多了容易晕。一开始我觉得写几个脚本就行,结果发现错了。业务逻辑耦合太紧,测试数据清理成了大问题。
核心问题在于测试用例之间的数据污染。如果用例 A 创建了用户,用例 B 依赖这个用户,一旦顺序变化就报错。为了解决这个问题,我在 conftest.py 里写了一个 session 级别的数据库连接 fixture,确保每个用例执行前后都清理数据。
```python
conftest.py
import pytestimport osfrom sqlalchemy import create_enginefrom sqlalchemy.orm import sessionmaker
@pytest.fixture(scope="session")def db_engine():engine = create_engine(os.getenv("DB_URL"))yield engineengine.dispose()
@pytest.fixturedef db_session(db_engine):Session = sessionmaker(bind=db_engine)session = Session()yield sessionsession.rollback()session.close()```
这段配置看起来简单,但当时我为了省事,没做 rollback,直接 delete。后来在 CI 里发现事务提交后 delete 失效,导致数据堆积。坑死了。最后改成 rollback 才稳定下来。还有参数化测试,我用 pytest.mark.parametrize 把不同权限的用户测试场景合并成了一个用例,代码量直接少了一半。另外,我引入了 pytest-html 插件生成报告,比默认的文本输出直观得多。客户看到彩色的报告时,脸色明显好看了不少。我特意把失败用例标红,成功用例标绿,视觉上冲击力很强。
CI 流水线集成与排错
框架搭好后,下一步是集成到 GitLab CI。客户用的是自建的 GitLab 服务器,Runner 是 Linux 环境。我原本以为只要配置好环境变量就能跑,结果第一次提交就失败了。
报错信息显示数据库连接超时。我检查了代码,本地明明能连。试了一圈发现,是 Runner 所在的机器没有配置内网 DNS 解析。开发环境能解析,CI 环境不行。这在本地开发时根本遇不到,因为本地直接走 hosts 或者能访问内网。当时我盯着日志看了半小时,以为是防火墙问题,后来才意识到是网络环境差异。日志里反复打印 Connection refused,让我误以为是端口没开。
我在 .gitlab-ci.yml 里加了一步安装 DNS 工具并验证连接,但这治标不治本。最终方案是在 Runner 的 hosts 文件里硬编码了数据库 IP。虽然不优雅,但稳定。客户那边网络策略管得严,改 DNS 配置需要走流程,硬编码是最快的解决方案。
```yaml
.gitlab-ci.yml
test:stage: testscript:
- pip install -r requirements.txt
- pytest tests/ –html=report.html –self-contained-html
artifacts:paths:
- report.html
only:
- main
```
有意思的是,客户后来要求把测试报告直接发到群里。我加了个脚本解析 HTML 报告,提取失败用例摘要,通过 Webhook 推送。这一步虽然增加了复杂度,但业务同事终于愿意看报告了。之前报告躺在服务器上没人看,现在直接推送到工作群,谁负责哪个模块一眼就能看见。还有一个小细节,依赖安装时间太长。我把 requirements.txt 里的包版本锁死了,并且配置了 pip 镜像源。CI 执行时间从 5 分钟降到了 2 分钟。客户对此很满意,因为开发速度提升了。
说实话,自动化测试不是搭好了就完事。后续维护才是大头。我留了一份文档给他们的测试同学,里面记录了常见报错和排查步骤。希望他们能真正用起来,而不是最后又变成另一个烂尾项目。这次项目最大的收获不是技术,而是明白了工具必须服务于人,而不是让人服务于工具。如果测试同学觉得写用例太麻烦,框架再好也是白搭。所以我特意简化了用例编写规范,只要继承基类就行。
本文基于实际项目经验整理,欢迎在评论区交流技术问题。

