简体   繁体   English

如何定义一个 pytest 函数来使用具有 datetime 类型字段的 pydantic 模型使用 FastAPI 测试端点

[英]How can define a pytest function to test an endpoint using FastAPI with a pydantic model that has a field of type datetime

I have a pytest function to test a FastAPI endpoint, this is just to verify that right data can create an entity, here the test function:我有一个 pytest 函数来测试 FastAPI 端点,这只是为了验证正确的数据可以创建一个实体,这里是测试函数:

import pytest
from databases import Database
from fastapi import FastAPI, status
from httpx import AsyncClient
from modules.member.members_schemas import MemberCreate

pytestmark = pytest.mark.asyncio

class TestCreateMember:
    async def test valid_input_creates_member(self, app: FastAPI, client: AsyncClient,
          db: Database) -> None:
        new_member = MemberCreate(
            fullname="John Smith"
            dni="123456789",
            birthdate=datetime(1985, 10, 4),
            email="testemail@test.com",
        )

        res = await  client.post(
             app.url_path_for("members:create-member), json={"member": new_member.dict()}
        )
        assert res.status_code == status.HTTP_201_CREATED

Here the pydantic model (schema in my case):这里是 pydantic 模型(在我的例子中是模式):

from datetime import datetime
from pydantic import BaseModel, BaseConfig, EmailStr

class BaseSchema(BaseModel):
    class Config(BaseConfig):
        allow_population_by_field_name = True
        orm_mode = True

class MemberCreate(BaseSchema):
    fullname: str
    dni: str
    birth_date: datetime
    email: EmailStr

And here is the endpoint to test:这是要测试的端点:

from databases import Database
from fastapi import APIRouter, Body, Depends, status

router = APIRouter(
    prefix="/members",
    tags=["members"],
    responses={404: {"description": "Not found"}},
)

@router.post(
    "/",
    response_model=MemberPublic,
    name="members:create-member",
    status_code=status.HTTP_201_CREATED,
)
async def create_member(
    member: MemberCreate = Body(..., embed=True),
    db: Database = Depends(get_database),
    current_user: UserInDB = Depends(get_current_active_user),
) -> ServiceResult:
    result = await MemberService(db).create_member(member, current_user)
    return handle_result(result)

ServiceResult type and handle_result() function are funcionalies to make a stardard answer from each endpoint made it, not problem working with it with a lot of other endpoints. ServiceResult 类型和 handle_result() 函数是从每个端点做出标准答案的函数,与许多其他端点一起使用没有问题。 When I ran pytest on this particular test, I got this error:当我在这个特定的测试上运行 pytest 时,我得到了这个错误:

self = <json.encoder.JSONEncoder object at 0x7fe1ebe3fd10>
o = datetime.datetime(1985, 10, 4, 0, 0)

    def default(self, o):
        """Implement this method in a subclass such that it returns
        a serializable object for ``o``, or calls the base implementation
        (to raise a ``TypeError``).
    
        For example, to support arbitrary iterators, you could
        implement default like this::
    
            def default(self, o):
                try:
                    iterable = iter(o)
                except TypeError:
                    pass
                else:
                    return list(iterable)
                # Let the base class default method raise the TypeError
                return JSONEncoder.default(self, o)
    
        """
>       raise TypeError(f'Object of type {o.__class__.__name__} '
                        f'is not JSON serializable')
E       TypeError: Object of type datetime is not JSON serializable

So I changed the test adding a json.dumps() (importing json, of course), this way:所以我改变了测试添加一个 json.dumps() (当然导入 json),这样:

    async def test valid_input_creates_member(self, app: FastAPI, client: AsyncClient,
          db: Database) -> None:
        new_member = MemberCreate(
            fullname="John Smith"
            dni="123456789",
            birthdate=datetime(1985, 10, 4),
            email="testemail@test.com",
        )

        new_member_json = json.dumps(new_member.dict(), indent=4, sort_keys=True, default=str)

        res = await  client.post(
             app.url_path_for("members:create-member), json={"member": new_member_json}
        )
        assert res.status_code == status.HTTP_201_CREATED

After I ran pytest again, I got this error new error:再次运行 pytest 后,我收到了这个错误新错误:

>       assert res.status_code == status.HTTP_201_CREATED
E       assert 422 == 201
E        +  where 422 = <Response [422 Unprocessable Entity]>.status_code
E        +  and   201 = status.HTTP_201_CREATED

Which is a Pydantic validation error at the endpoint.这是端点处的 Pydantic 验证错误。

When I see the endpoint post in the swagger view, the datetime field is presented as a string, so no problems to be serialized, but I don´t want to change the schema to receive a str, because I would lose the pydantic's power.当我在 swagger 视图中看到端点帖子时,日期时间字段显示为字符串,因此序列化没有问题,但我不想更改模式以接收 str,因为我会失去 pydantic 的力量。 So my question is, How can I set the test in order to override the pydantc validation and accept the datetime field?所以我的问题是,如何设置测试以覆盖 pydantc 验证并接受日期时间字段? or I must change the schema to have a string field and process it internally in order to receive only valid datetime data?.或者我必须将模式更改为具有字符串字段并在内部处理它以便仅接收有效的日期时间数据?。 I'll appreciate any help, because I'm stuck on this problem for several hours.我将不胜感激任何帮助,因为我在这个问题上停留了几个小时。

As suggets matslindh , I changed the test function, eliminating the pydantic object and it was substituted by a simple dict, this way:作为matslindh的建议,我更改了测试函数,消除了 pydantic 对象并将其替换为一个简单的字典,这样:

class TestCreateMember:
    async def test valid_input_creates_member(self, app: FastAPI, client: AsyncClient,
          db: Database) -> None:
        new_member = {
            "fullname": "John Smith"
            "dni": "123456789",
            "birthdate": "1985-10-04T00:00",
            "email": "testemail@test.com",
        }

        res = await  client.post(
             app.url_path_for("members:create-member), json={"member": new_member}
        )
        assert res.status_code == status.HTTP_201_CREATED

After that everything works as expected之后一切都按预期工作

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM