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

FastAPI与Pytest测试POST接口遇422错误,如何正确传参?

问题:FastAPI OAuth2登录接口测试报422错误

尝试使用FastAPI的TestClient测试/login相关的/token API,向POST接口传递JSON格式的username和password时,出现422错误,提示username和password字段为必填项。

API实现代码

@router.post('/token', response_model=schemas.Token)
async def login(user_credentials: OAuth2PasswordRequestForm = Depends(), db: Session = Depends(get_db)):
    """
    Login to the system with (Email | Username | Contact)
    """

    user = db.query(models.User).filter(
        (models.User.email == user_credentials.username) |
        (models.User.username == user_credentials.username) |
        (models.User.contact == user_credentials.username)
    ).first()

    if not user:
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN, detail="Invalid Credentials"
        )

    if not utils.verify_pwd(user_credentials.password, user.password):
        raise HTTPException(
            status_code=status.HTTP_403_FORBIDDEN, detail="Invalid Credentials"
        )

    access_token = oauth2.create_access_token(data={'user_id': user.id})

    return {"access_token": access_token, "token_type": "bearer"}

测试代码

from fastapi.testclient import TestClient

from ..main import app


client = TestClient(app)


def test_auth_token(get_client: TestClient):
    client = get_client.post('/token', json={"username": "admin", "password": "1234567890"})
    assert client.status_code == 200

错误信息

(venv)  ✘ genex@Genexs-MacBook-Pro: pytest -s
================================================================================== test session starts ===================================================================================
platform darwin -- Python 3.10.8, pytest-7.2.1, pluggy-1.0.0
rootdir: /Users/genex/Desktop/basha-bari
plugins: asyncio-0.20.3, anyio-3.6.2
asyncio: mode=strict
collected 1 item

apps/utility/test_utility_routers.py {'detail': [{'loc': ['body', 'username'], 'msg': 'field required', 'type': 'value_error.missing'}, {'loc': ['body', 'password'], 'msg': 'field required', 'type': 'value_error.missing'}]}
F

======================================================================================== FAILURES ========================================================================================
____________________________________________________________________________________ test_auth_token _____________________________________________________________________________________

    def test_auth_token():
        result = client.post('/token', json={"username": "admin", "password": "1234567890"})
        print(result.json())
>       assert result.status_code == 200
E       assert 422 == 200
E        +  where 422 = <Response [422 Unprocessable Entity]>.status_code

apps/utility/test_utility_routers.py:12: AssertionError
================================================================================ short test summary info =================================================================================
FAILED apps/utility/test_utility_routers.py::test_auth_token - assert 422 == 200
=================================================================================== 1 failed in 1.06s ====================================================================================

解决方法

直接修改测试代码(适配现有接口)

你的接口使用了OAuth2PasswordRequestForm作为请求依赖,这个类默认只接收**表单格式(application/x-www-form-urlencoded)**的请求体,不支持JSON格式。因此测试时需要把json参数换成data参数传递数据:

def test_auth_token(get_client: TestClient):
    # 用data参数传递表单格式数据,而非json
    response = get_client.post('/token', data={"username": "admin", "password": "1234567890"})
    assert response.status_code == 200

可选:修改接口支持JSON格式(如果需要)

如果希望接口同时支持JSON和表单格式,可以自定义一个Pydantic模型替代OAuth2PasswordRequestForm,或者结合两种格式的处理逻辑:

from pydantic import BaseModel

class LoginRequest(BaseModel):
    username: str
    password: str

@router.post('/token', response_model=schemas.Token)
async def login(
    form_data: OAuth2PasswordRequestForm = Depends(),
    json_data: LoginRequest = None,
    db: Session = Depends(get_db)
):
    # 优先取JSON数据,没有则用表单数据
    username = json_data.username if json_data else form_data.username
    password = json_data.password if json_data else form_data.password
    
    # 后续逻辑保持不变
    user = db.query(models.User).filter(
        (models.User.email == username) |
        (models.User.username == username) |
        (models.User.contact == username)
    ).first()
    
    # ... 其余验证和生成token逻辑

这样修改后,测试时既可以用data参数传表单,也可以用json参数传JSON数据。


内容的提问来源于stack exchange,提问作者Fahad Md Kamal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 13:07:53