README.md
July 6, 2026 · View on GitHub
Pytest integration for Modern-DI — turn any DI dependency into a pytest fixture with one line.
Installation
uv add --dev modern-di-pytest # or: pip install modern-di-pytest
Usage
The user owns the root container fixture. Pick whatever pytest scope fits the test suite:
# conftest.py
import typing
import modern_di
import pytest
from modern_di_pytest import expose, modern_di_fixture
from app import ioc
from app.ioc import Dependencies
from app.services import EmailClient
@pytest.fixture
def di_container() -> typing.Iterator[modern_di.Container]:
with modern_di.Container(groups=ioc.ALL_GROUPS, validate=True) as container:
yield container
# Bulk: every Provider on each group becomes a pytest fixture
# named after the class attribute. Pass several groups in one call.
expose(Dependencies)
# Manual: turn a single type or Provider into a named fixture.
email_client = modern_di_fixture(EmailClient)
Tests then receive resolved dependencies by name:
from app.services import EmailClient, UserService
def test_listing(user_service: UserService) -> None: # generated by expose(Dependencies)
assert user_service.list_users() == []
def test_email(email_client: EmailClient) -> None: # generated manually
email_client.send("hi")
Pointing a fixture at a child container
import typing
import modern_di
import pytest
from modern_di_pytest import modern_di_fixture
from app.services import UserService
@pytest.fixture
def request_container(
di_container: modern_di.Container,
) -> typing.Iterator[modern_di.Container]:
with di_container.build_child_container(scope=modern_di.Scope.REQUEST) as container:
yield container
request_user_service = modern_di_fixture(
UserService, container_fixture="request_container"
)
Overrides
Use Container.override() directly — modern-di already ships a first-class
override mechanism backed by a tree-shared OverridesRegistry:
import modern_di
from app.ioc import Dependencies
from app.services import UserService
from tests.fakes import FakeRepo
def test_with_override(
di_container: modern_di.Container,
user_service: UserService,
) -> None:
di_container.override(Dependencies.user_repo, FakeRepo())
try:
assert user_service.list_users() == []
finally:
di_container.reset_override(Dependencies.user_repo)
API
modern_di_fixture(dependency, *, container_fixture="di_container", name=None, pytest_scope="function")
Turn a single dependency into a pytest fixture. dependency is either a
type (resolved via container.resolve) or a Provider (resolved via
container.resolve_provider). The returned object is a real pytest fixture
— assign it to a module-level name and pytest will collect it.
expose(*groups, container_fixture="di_container", pytest_scope="function", module=None)
Walk each Group subclass in groups and inject one pytest fixture per
Provider class attribute into the caller's module. Fixture names equal the
class-attribute names. Non-Provider class attributes are skipped. A duplicate
attribute name across groups raises ValueError. Pass module=
explicitly when stack introspection cannot identify the caller.
📦 PyPI
📝 License
Part of modern-python
Built on modern-di, a dependency-injection framework with IoC container and scopes.
Browse the full list of templates and libraries in
modern-python — see the org profile for the categorized index.