集成测试必须隔离资源、禁用全局时钟、可替换服务依赖、显式管理数据生命周期、避免并行竞争。具体包括:用uuid隔离数据库schema/redis key/kafka topic;用事件驱动替代time.sleep();外部调用封装后mock并覆盖异常;启用真实db实例而非mock驱动;清理sql加where条件;唯一字段带测试标识;按资源分组执行;ci扫描硬编码值。

集成测试必须避开共享状态和全局时钟
分布式环境下,多个测试进程可能并发读写同一数据库、缓存或消息队列,导致结果不可预测。最常见现象是 test_order_processing_fails_when_stock_is_low 偶发失败,但单独跑又通过——本质是它和 test_place_order_successfully 竞争了同一条库存记录。
实操建议:
- 每个测试用例启动前,用独立命名空间隔离资源:数据库用
test_{uuid4()}作为 schema 名;Redis key 加前缀test:{uuid4()}:;Kafka topic 名动态生成并带测试 ID - 禁用
time.sleep()或依赖系统时间的断言(比如“5秒后订单状态应为 shipped”),改用事件驱动等待:监听order_shipped消息或轮询 API 返回status == "shipped",超时设为硬上限(如 30 秒) - 避免在
setUp()中预置固定 ID 的数据(如user_id = 123),所有 ID 必须由测试内创建时生成并显式引用
服务依赖要可替换且延迟可控
真实下游服务(支付网关、风控接口)响应慢、返回随机错误或根本不可达,会让集成测试变成定时炸弹。你不是在测自己的逻辑,而是在测网络抖动。
实操建议:
- 所有外部 HTTP 调用必须经过一个薄层封装(如 Python 的
PaymentClient类),测试时用unittest.mock.patch或responses库替换,不走真实网络 - 模拟响应必须覆盖边界:超时(
requests.exceptions.Timeout)、连接拒绝(ConnectionError)、HTTP 503、以及业务异常(如{"code": "INSUFFICIENT_BALANCE"}) - 不要 mock 数据库驱动本身(如
psycopg2.connect),而是用pytest-postgresql或testcontainers启一个真实 PostgreSQL 实例——mock 驱动会掩盖事务隔离、锁等待等分布式真实问题
测试数据生命周期必须显式管理
测试跑完没清理干净,残留订单、用户、消息堆积,下一轮测试就可能读到“上辈子”的数据。更糟的是,某些清理逻辑(如 DELETE FROM orders)在分布式事务中可能被回滚遗漏。
实操建议:
- 优先用“自动销毁”策略:容器化测试环境(Docker Compose)每次运行都启新实例,结束即
docker-compose down -v - 若必须复用环境,清理动作必须和创建动作成对出现,且放在
tearDown()或fixture的finalizer中,不能靠atexit或信号处理——进程被kill -9时它们不执行 - 清理 SQL 必须加 where 条件限定范围:
DELETE FROM orders WHERE created_at > '2024-01-01' AND test_run_id = 'abc123',绝不用无条件TRUNCATE
并行执行时资源竞争必须可检测
当你把测试从串行改成 pytest -n 4,突然一堆 IntegrityError: duplicate key value violates unique constraint 冒出来——说明多个进程在往同一张 users 表插相同邮箱。
实操建议:
- 所有唯一约束字段(email、order_no、external_id)必须带测试专属标识,比如邮箱生成为
f"user-{uuid4()}@test.example",而非"test@example.com" - 用
pytest-xdist的--dist=loadgroup+pytest.mark.group把强依赖同一资源的测试归组,确保它们不并行 - 在 CI 中加一道检查:运行前扫描所有测试文件,报出所有硬编码的唯一值(如正则匹配
r'email="test@.*?"'),这类代码直接标为阻塞项
真正难的不是让测试“能跑”,而是让它们在 20 个节点上同时跑 100 次都不互相污染。资源隔离粒度、清理时机、并发标识——这三处一旦松动,分布式测试集就会退化成概率性漏测工具。










