mirror of https://github.com/tiangolo/fastapi.git
✅ Use `inline-snapshot` to support different Pydantic versions in the test suite (#12534)
Co-authored-by: svlandeg <svlandeg@github.com> Co-authored-by: Sofie Van Landeghem <svlandeg@users.noreply.github.com>
This commit is contained in:
parent
79bc96647b
commit
ea42ebda80
|
|
@ -10,7 +10,7 @@ anyio[trio] >=3.2.1,<5.0.0
|
||||||
PyJWT==2.8.0
|
PyJWT==2.8.0
|
||||||
pyyaml >=5.3.1,<7.0.0
|
pyyaml >=5.3.1,<7.0.0
|
||||||
passlib[bcrypt] >=1.7.2,<2.0.0
|
passlib[bcrypt] >=1.7.2,<2.0.0
|
||||||
inline-snapshot==0.19.3
|
inline-snapshot>=0.21.1
|
||||||
# types
|
# types
|
||||||
types-ujson ==5.10.0.20240515
|
types-ujson ==5.10.0.20240515
|
||||||
types-orjson ==3.6.2
|
types-orjson ==3.6.2
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,13 @@ from dirty_equals import IsDict
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from inline_snapshot import snapshot
|
from inline_snapshot import snapshot
|
||||||
|
|
||||||
from tests.utils import needs_py39, needs_py310, needs_pydanticv1, needs_pydanticv2
|
from tests.utils import (
|
||||||
|
needs_py39,
|
||||||
|
needs_py310,
|
||||||
|
needs_pydanticv1,
|
||||||
|
needs_pydanticv2,
|
||||||
|
pydantic_snapshot,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(
|
@pytest.fixture(
|
||||||
|
|
@ -59,8 +65,8 @@ def test_cookie_param_model_defaults(client: TestClient):
|
||||||
def test_cookie_param_model_invalid(client: TestClient):
|
def test_cookie_param_model_invalid(client: TestClient):
|
||||||
response = client.get("/items/")
|
response = client.get("/items/")
|
||||||
assert response.status_code == 422
|
assert response.status_code == 422
|
||||||
assert response.json() == snapshot(
|
assert response.json() == pydantic_snapshot(
|
||||||
IsDict(
|
v2=snapshot(
|
||||||
{
|
{
|
||||||
"detail": [
|
"detail": [
|
||||||
{
|
{
|
||||||
|
|
@ -71,9 +77,8 @@ def test_cookie_param_model_invalid(client: TestClient):
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
),
|
||||||
| IsDict(
|
v1=snapshot(
|
||||||
# TODO: remove when deprecating Pydantic v1
|
|
||||||
{
|
{
|
||||||
"detail": [
|
"detail": [
|
||||||
{
|
{
|
||||||
|
|
@ -83,7 +88,7 @@ def test_cookie_param_model_invalid(client: TestClient):
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -144,19 +149,24 @@ def test_openapi_schema(client: TestClient):
|
||||||
"name": "fatebook_tracker",
|
"name": "fatebook_tracker",
|
||||||
"in": "cookie",
|
"in": "cookie",
|
||||||
"required": False,
|
"required": False,
|
||||||
"schema": IsDict(
|
"schema": pydantic_snapshot(
|
||||||
|
v2=snapshot(
|
||||||
{
|
{
|
||||||
"anyOf": [{"type": "string"}, {"type": "null"}],
|
"anyOf": [
|
||||||
|
{"type": "string"},
|
||||||
|
{"type": "null"},
|
||||||
|
],
|
||||||
"title": "Fatebook Tracker",
|
"title": "Fatebook Tracker",
|
||||||
}
|
}
|
||||||
)
|
),
|
||||||
| IsDict(
|
v1=snapshot(
|
||||||
# TODO: remove when deprecating Pydantic v1
|
# TODO: remove when deprecating Pydantic v1
|
||||||
{
|
{
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"title": "Fatebook Tracker",
|
"title": "Fatebook Tracker",
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "googall_tracker",
|
"name": "googall_tracker",
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import warnings
|
||||||
import pytest
|
import pytest
|
||||||
from dirty_equals import IsDict, IsInt
|
from dirty_equals import IsDict, IsInt
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from inline_snapshot import snapshot
|
from inline_snapshot import Is, snapshot
|
||||||
from sqlalchemy import StaticPool
|
from sqlalchemy import StaticPool
|
||||||
from sqlmodel import SQLModel, create_engine
|
from sqlmodel import SQLModel, create_engine
|
||||||
from sqlmodel.main import default_registry
|
from sqlmodel.main import default_registry
|
||||||
|
|
@ -117,14 +117,14 @@ def test_crud_app(client: TestClient):
|
||||||
)
|
)
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
assert response.json() == snapshot(
|
assert response.json() == snapshot(
|
||||||
{"name": "Dog Pond", "age": None, "id": hero_id}
|
{"name": "Dog Pond", "age": None, "id": Is(hero_id)}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Get updated hero
|
# Get updated hero
|
||||||
response = client.get(f"/heroes/{hero_id}")
|
response = client.get(f"/heroes/{hero_id}")
|
||||||
assert response.status_code == 200, response.text
|
assert response.status_code == 200, response.text
|
||||||
assert response.json() == snapshot(
|
assert response.json() == snapshot(
|
||||||
{"name": "Dog Pond", "age": None, "id": hero_id}
|
{"name": "Dog Pond", "age": None, "id": Is(hero_id)}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Delete a hero
|
# Delete a hero
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import sys
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from fastapi._compat import PYDANTIC_V2
|
from fastapi._compat import PYDANTIC_V2
|
||||||
|
from inline_snapshot import Snapshot
|
||||||
|
|
||||||
needs_py39 = pytest.mark.skipif(sys.version_info < (3, 9), reason="requires python3.9+")
|
needs_py39 = pytest.mark.skipif(sys.version_info < (3, 9), reason="requires python3.9+")
|
||||||
needs_py310 = pytest.mark.skipif(
|
needs_py310 = pytest.mark.skipif(
|
||||||
|
|
@ -9,3 +10,25 @@ needs_py310 = pytest.mark.skipif(
|
||||||
)
|
)
|
||||||
needs_pydanticv2 = pytest.mark.skipif(not PYDANTIC_V2, reason="requires Pydantic v2")
|
needs_pydanticv2 = pytest.mark.skipif(not PYDANTIC_V2, reason="requires Pydantic v2")
|
||||||
needs_pydanticv1 = pytest.mark.skipif(PYDANTIC_V2, reason="requires Pydantic v1")
|
needs_pydanticv1 = pytest.mark.skipif(PYDANTIC_V2, reason="requires Pydantic v1")
|
||||||
|
|
||||||
|
|
||||||
|
def pydantic_snapshot(
|
||||||
|
*,
|
||||||
|
v2: Snapshot,
|
||||||
|
v1: Snapshot, # TODO: remove v1 argument when deprecating Pydantic v1
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
This function should be used like this:
|
||||||
|
|
||||||
|
>>> assert value == pydantic_snapshot(v2=snapshot(),v1=snapshot())
|
||||||
|
|
||||||
|
inline-snapshot will create the snapshots when pytest is executed for each versions of pydantic.
|
||||||
|
|
||||||
|
It is also possible to use the function inside snapshots for version-specific values.
|
||||||
|
|
||||||
|
>>> assert value == snapshot({
|
||||||
|
"data": "some data",
|
||||||
|
"version_specific": pydantic_snapshot(v2=snapshot(),v1=snapshot()),
|
||||||
|
})
|
||||||
|
"""
|
||||||
|
return v2 if PYDANTIC_V2 else v1
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue