pytest¶
Built-in pytest plugin (Injected parameters)¶
diwire ships with an optional pytest plugin that resolves parameters annotated as
Annotated[T, Injected()] from a container.
Enable it in a test module or conftest.py:
pytest_plugins = ["diwire.integrations.pytest_plugin"]
Then annotate parameters:
from typing import Annotated
from diwire import Injected
def test_example(service: Annotated["Service", Injected()]) -> None:
assert service is not None
Customizing the container¶
The plugin uses a diwire_container fixture. Override it to register fakes and test-specific
configuration:
import pytest
from diwire import Container, Lifetime
@pytest.fixture()
def diwire_container() -> Container:
container = Container(autoregister=False)
container.register(Service, concrete_class=FakeService, lifetime=Lifetime.SINGLETON)
return container
Scopes for injected dependencies¶
By default, injected dependencies are resolved inside container.enter_scope("test_function").
You can configure the scope name in four ways (highest priority first):
@pytest.mark.diwire_scope("my_scope")(orNoneto disable scoping for a single test)CLI option:
--diwire-scope my_scopeOverride the
diwire_scopefixture (per test module / conftest)diwire_scopeini value (default istest_function)
Example (ini):
[tool.pytest.ini_options]
diwire_scope = "request"
To disable scoping entirely:
import pytest
@pytest.fixture()
def diwire_scope() -> str | None:
return None
Notes¶
The plugin removes injected parameters from pytest’s fixture signature so normal fixture discovery still works.
The plugin is loaded via
pytest_plugins; it is not auto-registered.
Container fixture¶
import pytest
from diwire import Container
@pytest.fixture
def container() -> Container:
# Prefer a fresh container per test.
return Container()
Using container_context in tests¶
If your app uses diwire.container_context, set/reset it in a fixture:
import pytest
from diwire import Container, container_context
@pytest.fixture
def container() -> Container:
container = Container()
token = container_context.set_current(container)
try:
yield container
finally:
container_context.reset(token)
Cleaning up scopes¶
Prefer with container.enter_scope(...): in tests so scope cleanup is deterministic.
If you create scopes imperatively, close them (or call diwire.Container.close()) in teardown.