You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在FastAPI/Starlette测试客户端中设置Request对象的state属性?

解决FastAPI测试中request.state为空的问题

问题根源

你的端点依赖request.state.token,但这个值通常是由中间件在请求进入端点前从Authorization头提取并设置的。测试时你虽然传了Authorization头,但测试用的TestClient对应的App实例没有加载这个处理token的中间件,导致request.state为空。

解决方案

方案1:确保测试App加载了真实的认证中间件

如果你的项目中已经有处理Authorization头并设置request.state.token的中间件(比如在main.py中添加的),测试时必须基于这个完整的App创建TestClient,而不是空的App。

示例:

# 测试文件中导入带中间件的App
from main import app
from starlette.testclient import TestClient

# 创建TestClient时使用完整的App
my_test_client = TestClient(app)

def test_get_data(my_test_client: TestClient, api_url: str, api_token: str) -> None:
    params = {
        "offset": 0,
        "limit": 20,
    }
    # 注意:如果中间件要求Authorization头是"Bearer {token}"格式,要确保api_token符合格式
    response = my_test_client.get(url=api_url, headers={"Authorization": f"Bearer {api_token}"}, params=params)
    assert response.status_code == HTTP_200_OK

方案2:测试时添加临时中间件

如果不想依赖真实中间件,可以在测试函数中临时添加一个中间件,模拟token提取逻辑:

from starlette.middleware.base import BaseHTTPMiddleware
from fastapi import Request

def test_get_data(my_test_client: TestClient, api_url: str, api_token: str) -> None:
    # 定义临时中间件:从Authorization头提取token并设置到request.state
    async def temp_auth_middleware(request: Request, call_next):
        auth_header = request.headers.get("Authorization")
        if auth_header:
            # 根据实际格式处理,这里假设api_token是完整的Bearer令牌
            request.state.token = auth_header
            # 如果需要拆分Bearer前缀:_, token = auth_header.split(); request.state.token = token
        response = await call_next(request)
        return response

    # 将临时中间件添加到测试App
    my_test_client.app.add_middleware(BaseHTTPMiddleware, dispatch=temp_auth_middleware)

    params = {
        "offset": 0,
        "limit": 20,
    }
    response = my_test_client.get(url=api_url, headers={"Authorization": api_token}, params=params)
    assert response.status_code == HTTP_200_OK

方案3:用依赖覆盖简化测试

把提取token的逻辑抽成独立依赖,测试时直接覆盖这个依赖返回测试用token,无需处理request.state:

第一步:改造端点代码,抽离token依赖

from fastapi import Depends, Request

# 抽离token提取逻辑为依赖
def get_request_token(request: Request):
    return request.state.token

@router.get("/", response_model=DataOut)
def get_documents(
    query_params: QueryParams = Depends(),
    token: str = Depends(get_request_token)  # 注入token依赖
) -> DataOut:
    return DocumentsOut.parse_obj(
        data_api_client.get(
            "/data/",
            token,
            params=query_params.dict(by_alias=True),
        ).json()
    )

第二步:测试中覆盖依赖

def test_get_data(my_test_client: TestClient, api_url: str, api_token: str) -> None:
    # 覆盖get_request_token依赖,直接返回测试token
    def override_token():
        return api_token

    my_test_client.app.dependency_overrides[get_request_token] = override_token

    params = {
        "offset": 0,
        "limit": 20,
    }
    # 此时无需传Authorization头,依赖直接返回测试token
    response = my_test_client.get(url=api_url, params=params)
    assert response.status_code == HTTP_200_OK

    # 测试后清除依赖覆盖,避免影响其他测试
    my_test_client.app.dependency_overrides.clear()

内容的提问来源于stack exchange,提问作者Okroshiashvili

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 19:20:23