Starlette TestClient 升级 HTTPX 后报 unexpected keyword argument app
HTTPX 0.28 删除 app 参数后,旧 Starlette TestClient 可能在构造阶段失败。核对实际测试环境、框架约束与锁文件,再验证 lifespan 和原测试。
测试代码还没发出请求,创建 TestClient(app) 就报 TypeError: Client.__init__() got an unexpected keyword argument 'app'。如果环境里是 Starlette 0.36.3 与 HTTPX 0.28.0,这组版本存在已知的接口冲突:旧 TestClient 向 HTTPX Client 传入了后者已删除的 app 参数。
这里讨论构造测试客户端时的异常。应用启动失败、请求返回 500、测试事件循环冲突,要沿各自的 traceback 排查,不能都归到这组依赖上。
确认报错来自哪一套环境
在运行测试的同一个虚拟环境中查看版本与依赖关系:
python -m pip show starlette httpx fastapi
python -m pip check
没有使用 FastAPI 的项目,可以忽略它未安装的提示。测试通过编辑器或 CI 启动时,还要核对对应的 Python 路径;终端里查看的依赖未必就是测试环境的依赖。解释器与 pip 归属的核对方法可以用于确认这一点。
traceback 应指向 Starlette TestClient 调用 HTTPX 的构造函数,而不是你自己写的 httpx.Client(app=...)。HTTPX 0.27.0 已弃用 app 参数,0.28.0 删除了它;Starlette 0.37.2 则移除了这一项传参,继续使用它自己的测试 transport。
在项目支持的版本范围内升级
直接使用 Starlette 的项目,可以把 Starlette 调整到包含修复的版本。以修复发生的版本作隔离验证时,Starlette 0.37.2 已去掉冲突参数;这不等于要求所有项目永久锁在这个旧版本。
若项目通过 FastAPI 间接依赖 Starlette,先核对当前 FastAPI 声明的 Starlette 范围。需要同时升级框架时,一起调整依赖声明和锁文件,不要强行安装一个超出框架约束的 Starlette,再忽略 pip check 的冲突。
升级受其他依赖限制时,可以暂时将 HTTPX 限制在 0.28 之前,例如在项目约束文件中使用:
httpx<0.28
这只是兼容过渡。旧 TestClient 仍会使用已经弃用的调用形式,应记录解除限制所需的框架升级。提交依赖改动前保留原锁文件;出现其他兼容问题时恢复原文件,再用项目原有的安装工具恢复环境。
用一个请求验证,再跑原测试
下面的示例只需要 Starlette 与兼容版本的 HTTPX。使用上下文管理器,可以让应用的 lifespan 随客户端进入与退出:
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Route
from starlette.testclient import TestClient
async def health(request):
return PlainTextResponse("ok")
app = Starlette(routes=[Route("/health", health)])
with TestClient(app) as client:
response = client.get("/health")
assert response.status_code == 200
assert response.text == "ok"
构造客户端不再抛出 app 参数异常,只说明这项接口冲突已解除。还要跑回原来的测试,检查启动与关闭逻辑、认证、异常传播以及项目真正使用的请求行为。不要在已安装的包里手动删参数作为长期修复;这种改动不会进入依赖锁定记录,也可能在重装环境时消失。


