Reconciling pytest fixture injection with app's own service dependency injection system #15076
Replies: 1 comment 1 reply
|
Option 1 is actually a legitimate, well-behaved way to do this — pytest discovers fixtures by scanning the conftest module's attributes, so it doesn't care whether a fixture was written by hand or assigned in a loop. The trick is to generate them through a small factory and give each one an explicit # conftest.py
import pytest
from app.container import build_container
from app.protocols import IServiceA, IServiceB # ...
# fixture name -> interface; could also be derived from the registry itself
SERVICES = {
"service_a": IServiceA,
"service_b": IServiceB,
}
@pytest.fixture
def app_container():
container = build_container() # however the app builds it
yield container
container.close() # if it has teardown
def _make_service_fixture(name, iface):
@pytest.fixture(name=name)
def _fixture(app_container):
return app_container.get(iface)
_fixture.__doc__ = f"{iface.__name__} resolved from the app container."
return _fixture
for _name, _iface in SERVICES.items():
globals()[_name] = _make_service_fixture(_name, _iface)Tests then look exactly like your Option 0 example — A few things that make this nicer in practice:
I'd avoid Option 2 (wrapping tests / |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
I have a web app that has a service registry/container that maps an interface to factory that is cached after construction, depending on the scope. In a way it is similar to pytest fixtures themselves. When I'm writing tests I want to load those services alongside fixtures. Or really just "auto" make the fixtures.
What is a good way to do that? I think maybe just hard coding all the fixtures is the easiest way (after writing this up). It always seems like it would be repetitive but it would probably be write once use until the actual service(s) changed.
Some options I've tried:
globals()via some module level code inconftest.pyglobals()[service_fixture_name] = service_fixture_funcinconftest.pypytest_generate_teststo parameterize in the app service fixtures?Option 0
Edited: Try to fix formatting.
All reactions