Skip to content

Commit d5ce2c3

Browse files
committed
Add type hints to conftest.py
1 parent 23a1c0a commit d5ce2c3

File tree

2 files changed

+47
-20
lines changed

2 files changed

+47
-20
lines changed

docs/index.md

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,9 @@ The `postgres_container` will be passed to `async_session`, which will be used i
314314

315315
The first fixture inserted in `conftest.py` is the `anyio_backend`, highlighted in the code below. This function will be used in `postgres_container` and marked for the AnyIO pytest plugin, as well as setting `asyncio` as the backend to run the tests. This function was not included in the previous diagram because it is an AnyIO specification. You can check more details about it [here](https://anyio.readthedocs.io/en/stable/testing.html#specifying-the-backends-to-run-on).
316316

317-
```py title="tests/conftest.py" linenums="1" hl_lines="15-17"
317+
```py title="tests/conftest.py" linenums="1" hl_lines="17-19"
318+
import typing
319+
318320
import pytest
319321
from httpx import ASGITransport, AsyncClient
320322
from sqlalchemy.ext.asyncio import (
@@ -330,26 +332,31 @@ from src.models import table_register
330332

331333

332334
@pytest.fixture
333-
def anyio_backend():
335+
def anyio_backend() -> str:
334336
return 'asyncio'
337+
335338
```
336339

337340
Now, in the `postgres_container`, the `anyio_backend` is passed, and all the tests that use the `postgres_container` as a fixture at any level will be marked to run asynchronously.
338341

339342
Below is the `postgres_container` function, which will be responsible for creating the `PostgresContainer` instance from `testcontainers`. The `asyncpg` driver is passed as an argument to specify that it will be the driver used.
340343

341-
```py title="tests/conftest.py" linenums="20"
344+
```py title="tests/conftest.py" linenums="22"
342345
@pytest.fixture
343-
def postgres_container(anyio_backend):
346+
def postgres_container(
347+
anyio_backend: typing.Literal['asyncio']
348+
) -> typing.Generator[PostgresContainer, None, None]:
344349
with PostgresContainer('postgres:16', driver='asyncpg') as postgres:
345350
yield postgres
346351
```
347352

348353
The `async_session` takes the connection URL from the `PostgresContainer` object returned by the `postgres_container` function and uses it to create the tables inside the database, as well as the session that will handle all interactions with the PostgreSQL instance created. The function will return and persist a session to be used, and then restore the database for the next test by deleting the tables.
349354

350-
```py title="tests/conftest.py" linenums="26"
355+
```py title="tests/conftest.py" linenums="30"
351356
@pytest.fixture
352-
async def async_session(postgres_container: PostgresContainer):
357+
async def async_session(
358+
postgres_container: PostgresContainer
359+
) -> typing.AsyncGenerator[AsyncSession, None]:
353360
async_db_url = postgres_container.get_connection_url()
354361
async_engine = create_async_engine(async_db_url, pool_pre_ping=True)
355362

@@ -372,9 +379,11 @@ async def async_session(postgres_container: PostgresContainer):
372379

373380
The last fixture is the `async_client` function, which will create the [`AsyncClient`](https://fastapi.tiangolo.com/advanced/async-tests/), directly imported from [HTTPX](https://www.python-httpx.org/), and provide it to make requests to our endpoints. Here, the session provided by `async_session` will override the session originally used in our app as a dependency injection while the client is being used.
374381

375-
```py title="tests/conftest.py" linenums="48"
382+
```py title="tests/conftest.py" linenums="54"
376383
@pytest.fixture
377-
async def async_client(async_session: async_sessionmaker[AsyncSession]):
384+
async def async_client(
385+
async_session: AsyncSession
386+
) -> typing.AsyncGenerator[AsyncClient, None]:
378387
app.dependency_overrides[get_session] = lambda: async_session
379388
_transport = ASGITransport(app=app)
380389

@@ -494,16 +503,19 @@ Fixtures are created when first requested by a test and are destroyed based on t
494503

495504
As we want to create just one Docker instance and reuse it for all the tests, we changed the `@pytest.fixture` in the `conftest.py` file in the following highlighted lines.
496505

497-
```py title="conftest.py" linenums="25" hl_lines="1 6"
506+
```py title="conftest.py" linenums="17" hl_lines="1 6"
498507
@pytest.fixture(scope='session')
499-
def anyio_backend():
508+
def anyio_backend() -> str:
500509
return 'asyncio'
501510

502511

503512
@pytest.fixture(scope='session')
504-
def postgres_container(anyio_backend):
513+
def postgres_container(
514+
anyio_backend: typing.Literal['asyncio']
515+
) -> typing.Generator[PostgresContainer, None, None]:
505516
with PostgresContainer('postgres:16', driver='asyncpg') as postgres:
506517
yield postgres
518+
507519
```
508520

509521
Now, every time we run the tests, we will follow a workflow similar to the one below, where the `postgres_container` fixture is created only once at the beginning of the test session and is reused in all other fixtures. The `async_session` and `async_client` fixtures are still created and destroyed for each test. The `postgres_container` fixture is destroyed only after all the tests have finished.
@@ -583,6 +595,8 @@ tests/test_routes.py::test_buy_ticket_when_already_sold PASSED [100%]
583595
The final `conftest.py` is presented below:
584596

585597
```py title="tests/conftest.py" linenums="1"
598+
import typing
599+
586600
import pytest
587601
from httpx import ASGITransport, AsyncClient
588602
from sqlalchemy.ext.asyncio import (
@@ -598,18 +612,22 @@ from src.models import table_register
598612

599613

600614
@pytest.fixture(scope='session')
601-
def anyio_backend():
615+
def anyio_backend() -> str:
602616
return 'asyncio'
603617

604618

605619
@pytest.fixture(scope='session')
606-
def postgres_container(anyio_backend):
620+
def postgres_container(
621+
anyio_backend: typing.Literal['asyncio']
622+
) -> typing.Generator[PostgresContainer, None, None]:
607623
with PostgresContainer('postgres:16', driver='asyncpg') as postgres:
608624
yield postgres
609625

610626

611627
@pytest.fixture
612-
async def async_session(postgres_container: PostgresContainer):
628+
async def async_session(
629+
postgres_container: PostgresContainer
630+
) -> typing.AsyncGenerator[AsyncSession, None]:
613631
async_db_url = postgres_container.get_connection_url()
614632
async_engine = create_async_engine(async_db_url, pool_pre_ping=True)
615633

@@ -631,7 +649,9 @@ async def async_session(postgres_container: PostgresContainer):
631649

632650

633651
@pytest.fixture
634-
async def async_client(async_session: async_sessionmaker[AsyncSession]):
652+
async def async_client(
653+
async_session: AsyncSession
654+
) -> typing.AsyncGenerator[AsyncClient, None]:
635655
app.dependency_overrides[get_session] = lambda: async_session
636656
_transport = ASGITransport(app=app)
637657

@@ -641,5 +661,4 @@ async def async_client(async_session: async_sessionmaker[AsyncSession]):
641661
yield client
642662

643663
app.dependency_overrides.clear()
644-
645664
```

tests/conftest.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import typing
2+
13
import pytest
24
from httpx import ASGITransport, AsyncClient
35
from sqlalchemy.ext.asyncio import (
@@ -13,18 +15,22 @@
1315

1416

1517
@pytest.fixture(scope='session')
16-
def anyio_backend():
18+
def anyio_backend() -> str:
1719
return 'asyncio'
1820

1921

2022
@pytest.fixture(scope='session')
21-
def postgres_container(anyio_backend):
23+
def postgres_container(
24+
anyio_backend: typing.Literal['asyncio']
25+
) -> typing.Generator[PostgresContainer, None, None]:
2226
with PostgresContainer('postgres:16', driver='asyncpg') as postgres:
2327
yield postgres
2428

2529

2630
@pytest.fixture
27-
async def async_session(postgres_container: PostgresContainer):
31+
async def async_session(
32+
postgres_container: PostgresContainer
33+
) -> typing.AsyncGenerator[AsyncSession, None]:
2834
async_db_url = postgres_container.get_connection_url()
2935
async_engine = create_async_engine(async_db_url, pool_pre_ping=True)
3036

@@ -46,7 +52,9 @@ async def async_session(postgres_container: PostgresContainer):
4652

4753

4854
@pytest.fixture
49-
async def async_client(async_session: async_sessionmaker[AsyncSession]):
55+
async def async_client(
56+
async_session: AsyncSession
57+
) -> typing.AsyncGenerator[AsyncClient, None]:
5058
app.dependency_overrides[get_session] = lambda: async_session
5159
_transport = ASGITransport(app=app)
5260

0 commit comments

Comments
 (0)