11import contextlib
22import json
3- from collections .abc import AsyncGenerator , AsyncIterator , Iterable , Iterator
3+ from collections .abc import AsyncIterator , Iterable , Iterator
44from pathlib import Path
55from typing import Any , NamedTuple
66
99import pytest
1010from _pytest .config import Config
1111from _pytest .nodes import Item
12+ from asgi_lifespan import LifespanManager
13+ from fastapi import FastAPI
1214from sqlalchemy import text
1315from sqlalchemy .ext .asyncio import AsyncConnection , AsyncEngine
1416
1517from database .setup import expdb_database , user_database
16- from main import create_api , lifespan
18+ from main import create_api
1719from routers .dependencies import expdb_connection , userdb_connection
1820
1921PHP_API_URL = "http://php-api:80/api/v1/json"
@@ -51,12 +53,6 @@ async def temporary_records(
5153 await connection .commit ()
5254
5355
54- @pytest .fixture (autouse = True , scope = "session" )
55- async def one_lifespan () -> AsyncGenerator [None , None ]:
56- async with lifespan (app = None ):
57- yield
58-
59-
6056@pytest .fixture
6157async def expdb_test () -> AsyncIterator [AsyncConnection ]:
6258 async with automatic_rollback (expdb_database ()) as connection :
@@ -75,14 +71,26 @@ async def php_api() -> AsyncIterator[httpx.AsyncClient]:
7571 yield client
7672
7773
74+ @pytest .fixture (scope = "session" )
75+ async def app () -> AsyncIterator [FastAPI ]:
76+ _app = create_api (Path (__file__ ).parent / "config.test.toml" )
77+ async with LifespanManager (_app ):
78+ yield _app
79+
80+
7881@pytest .fixture
7982async def py_api (
80- expdb_test : AsyncConnection , user_test : AsyncConnection
83+ expdb_test : AsyncConnection , user_test : AsyncConnection , app : FastAPI
8184) -> AsyncIterator [httpx .AsyncClient ]:
82- app = create_api (Path (__file__ ).parent / "config.test.toml" )
85+ """Create test client which automatically rolls back database updates on teardown."""
86+ # Using the function-scoped database fixtures automatically benefits the
87+ # automatic rollbacks, but also lets a test author write to a database
88+ # transaction that is shared with the app. That is, it enables:
89+ #
90+ # def my_test(expdb_test, py_api):
91+ # expdb_test.execute(...) # write some data # noqa: ERA001
92+ # py_api.get(...) # read that data # noqa: ERA001
8393
84- # We use async generator functions because fixtures may not be called directly.
85- # The async generator returns the test connections for FastAPI to handle properly
8694 async def override_expdb () -> AsyncIterator [AsyncConnection ]:
8795 yield expdb_test
8896
@@ -91,6 +99,7 @@ async def override_userdb() -> AsyncIterator[AsyncConnection]:
9199
92100 app .dependency_overrides [expdb_connection ] = override_expdb
93101 app .dependency_overrides [userdb_connection ] = override_userdb
102+
94103 # We do not use the Lifespan manager for now because our auto-use fixture
95104 # `one_lifespan` will do setup and teardown at a session scope level instead.
96105 async with httpx .AsyncClient (
0 commit comments