pytest集成测试端口冲突需动态分配并确保服务器就绪后请求,数据库须隔离,json请求要设正确header,清理资源防ci失败。

pytest 启动测试服务器时端口被占怎么办
本地跑集成测试经常卡在 Address already in use,本质是没做端口清理或复用控制。pytest 进程退出后,子进程(比如 Flask 或 FastAPI 的 dev server)可能还在后台占着端口,尤其用 subprocess.Popen 直接启动又没设 preexec_fn=os.setsid 时更明显。
实操建议:
- 测试前用
socket.bind()尝试绑定随机端口,再把端口号传给测试服务器(比硬写8000更可靠) - 用
pytest --tb=short -s跑一次看真实错误输出,确认是Address already in use还是Connection refused—— 后者大概率服务根本没起来 - 在
conftest.py里用pytest.fixture(scope="session")启停服务器,配合atexit.register()或finally确保关闭 - 避免用
os.system("uvicorn app:app &")这种 shell 后台方式,改用subprocess.Popen(..., stdout=subprocess.PIPE, stderr=subprocess.STDOUT)并保留句柄用于后续.terminate()
如何让 pytest 等待服务器真正就绪再发请求
HTTP 客户端(比如 requests.get("http://localhost:8000/health"))发得太早,会收到 ConnectionRefusedError。这不是网络问题,是服务器进程刚 fork 出来、还没完成 socket listen。
实操建议:
- 别用固定
time.sleep(1),不同机器启动速度差异大;改用轮询 + 超时:每 0.1 秒尝试一次requests.get,最多等 5 秒 - 健康检查接口要轻量,FastAPI 里直接返回
{"status": "ok"},不要连 DB 或查缓存 - 如果用的是
testclient(如TestClient(app)),它不走网络,不存在等待问题 —— 但这就不是端到端了,只是单元测试层级 - 轮询代码别塞进每个测试函数,抽成 fixture,比如叫
live_server,依赖它的地方自动获得已就绪的base_url
测试数据库状态隔离失败的典型表现
多个测试共用一个数据库(比如 SQLite 文件或 Docker 里的 PostgreSQL),导致 A 测试删了用户,B 测试却查不到预期数据,报 AssertionError。这不是 pytest 的问题,是测试环境没和业务逻辑解耦。
实操建议:
- 每次测试用独立数据库名(PostgreSQL)或独立 SQLite 文件路径(
sqlite:///test_{uuid4()}.db),而不是复用test.db - 用
pytest.fixture(scope="function")在每个测试前后执行Base.metadata.create_all()和Base.metadata.drop_all(),确保干净 schema - 如果业务代码里写了
engine = create_engine("sqlite:///prod.db")这种硬编码,测试根本绕不开 —— 必须改成从配置或环境变量读取 URL - Docker Compose 启的 DB,记得在
teardown阶段用docker-compose down -v清 volume,否则下次测试还带着旧数据
为什么 requests.post 传 JSON 却收到 400 Bad Request
常见于 FastAPI/Flask 接口定义了 pydantic.BaseModel 或 @app.route(..., methods=["POST"]) 但没设 Content-Type: application/json,或者 JSON 字段名跟模型字段对不上。
实操建议:
- 用
requests.post(url, json={...})(注意是json=参数),它会自动加 header、序列化;别用data=json.dumps({...})忘了设 header - 检查服务端日志,FastAPI 默认会在 400 时打印 validation error detail,比如
"field required at body.user_id",说明字段名大小写或嵌套层级错了 - 如果用 pytest-asyncio 测异步服务,别混用
requests(同步)和aiohttp(异步)客户端,容易卡死或超时 - 测试中打印
response.text和response.status_code,比只看assert response.ok更快定位是 422 还是 500
端到端集成测试最难的从来不是启动服务器,而是让“启动 → 就绪 → 执行 → 清理”这四个环节都稳定可重现。特别是跨进程通信、资源释放时机、环境变量污染这三类问题,往往在 CI 上才暴露,本地却一直通过。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











