FastAPI独立进程pytest测试失败排查及API测试方案咨询
FastAPI API测试问题求助
我正在为FastAPI编写API测试,通过独立进程启动服务,用requests发请求验证结果。提取关键代码做成最小复现示例后,直接运行主文件一切正常,但执行pytest测试时请求超时失败。想请教:
- 测试失败的原因是什么?
- 正确的API测试方式应该是怎样的?
代码详情
README.md
# Minimal example to ask in StackOverflow Pytest does not allow to start a process with a service and tests requests to it, at least not in the most straightforward way IMHO. I may be missing something, or I may be doing something wrong. Hence, I share this short code in a gist, to ask. ## How to run it The dependencies are: `fastapi pytest requests uvicorn`. You may install them with your package / environment manager of choice, or use `pipenv install` with the provided `Pipfile`. To run the code in the environment (e.g. `pipenv shell`), run: `python3 mwe.py`. You should see everything is `OK`. To run the test, run in the environment: `pytest`. This does not work for me, the request times out.
mwe.py
import fastapi, multiprocessing, requests, time, uvicorn app = fastapi.FastAPI() @app.get('/ok') def ok(): return 'OK' class service: def __enter__(self): def run_service(): uvicorn.run('mwe:app', host='0.0.0.0', port=8000, reload=True) self.service = multiprocessing.Process(target=run_service) self.service.start() time.sleep(10) def __exit__(self, *args): self.service.terminate() def main(): with service(): return requests.get('http://127.0.0.1:8000/ok').ok if __name__ == '__main__': print('🆖🆗'[main()])
Pipfile
[[source]] url = "https://pypi.org/simple" verify_ssl = true name = "pypi" [packages] pytest = "*" fastapi = "*" requests = "*" uvicorn = "*" [requires] python_version = "3.10" python_full_version = "3.10.6"
test_mwe.py
from mwe import main def test_main(): assert main()
问题解答
1. 测试失败的原因
reload=True的多进程干扰:uvicorn的热重载模式会启动额外子进程监控文件变化,在pytest的进程环境中,这种嵌套进程会导致服务无法正常初始化,最终服务未真正启动,请求超时。- 固定等待不可靠:
time.sleep(10)是固定时长等待,无法确保服务完成启动,测试环境中资源调度可能更慢,导致等待时间不足。 - 进程环境冲突:pytest运行测试时的进程上下文,可能导致
multiprocessing.Process启动的服务进程无法正确加载应用实例或监听端口。
2. 正确的API测试方式
FastAPI官方推荐两种测试方式,根据需求选择:
方案一:使用TestClient(推荐)
无需启动真实HTTP服务,直接通过TestClient模拟请求,测试效率更高、更稳定,这是FastAPI的标准测试方案。
修改后的核心代码:
# mwe.py import fastapi, requests, uvicorn from httpx import TestClient app = fastapi.FastAPI() @app.get('/ok') def ok(): return 'OK' # 创建TestClient实例 client = TestClient(app) def main(): # 本地运行时用真实请求,测试环境用TestClient try: return requests.get('http://127.0.0.1:8000/ok').ok except requests.exceptions.ConnectionError: response = client.get('/ok') return response.status_code == 200 # 保留原有的服务启动逻辑用于本地运行 class Service: def __enter__(self): def run_service(): # 关闭热重载避免多进程问题 uvicorn.run('mwe:app', host='0.0.0.0', port=8000, reload=False) import multiprocessing self.service = multiprocessing.Process(target=run_service) self.service.start() # 用端口轮询替代固定等待,确保服务启动 import time import socket def wait_for_port(port, timeout=30): start_time = time.time() while time.time() - start_time < timeout: try: with socket.create_connection(('127.0.0.1', port), timeout=1): return True except (ConnectionRefusedError, socket.timeout): time.sleep(0.5) return False wait_for_port(8000) def __exit__(self, *args): self.service.terminate() if __name__ == '__main__': with Service(): print('🆖🆗'[main()])
测试代码简化为:
# test_mwe.py from mwe import client def test_ok_endpoint(): response = client.get('/ok') assert response.status_code == 200 assert response.text == '"OK"'
方案二:修复真实服务启动逻辑
如果必须启动真实HTTP服务测试,需要调整以下几点:
- 关闭uvicorn的
reload模式(设为reload=False),避免多进程嵌套。 - 用端口轮询替换
time.sleep,确保服务真正启动后再发请求。 - 确保测试结束后正确终止服务进程,避免端口残留。
修改后的service类可参考方案一中的Service类,测试代码保持原有的test_main即可。
内容的提问来源于stack exchange,提问作者Trylks
相关产品推荐
相关产品推荐

