Open sonthonaxrk opened 5 years ago
To the degree I'm familiar with pytest fixtures (which is not much, I avoid them) I don't think I have a solution, maybe someone else reading this will be able to help.
I've used pytest quite a bit, and I don't think this is currently possible. Perhaps it would be possible to solve it with a plugin somehow. It would get even more complicated if the Injector instance would need to have test-specific configuration, which in my experience is likely.
I've usually resorted to a fixture for injector and sometimes other fixtures that gets instances of classes from that injector instance.
Hi,
I'm the author of an alternative dependency injector library (dependencies part of the dry-python project).
We already have integration with pytest suite.
We decide not to fight with pytest itself but provide a way to register fixture with injector outside of the pytest run function.
If anyone is interested to backport this to this project, I invite you to take a look at our implementation.
Best regards, Artem.
What about this solution?
def with_injection(f):
f = inject(f)
def wrapper():
injector = Injector([BaseModule(), StubModule()])
return injector.call_with_injection(f)
return wrapper
@with_injection
def test_foo(session: Session):
# session is injected!
...
Yeah, this is a good way to go about doing this.
Edit: As long as one's fine with separate Injector (with new scopes) per test.
Edit: As long as one's fine with separate Injector (with new scopes) per test.
Well yes, if that's not good for you, you can:
injector = Injector([BaseModule(), StubModule()])
def with_injection(f):
f = inject(f)
def wrapper():
return injector.call_with_injection(f)
return wrapper
@with_injection
def test_foo(session: Session):
# session is injected!
...
or maybe:
def with_injector(injector):
def decorator(f):
f = inject(f)
def wrapper():
return injector.call_with_injection(f)
return wrapper
return decorator
injector = Injector([BaseModule(), StubModule()])
@with_injector(injector)
def test_foo(session: Session):
# session is injected!
...
# alternative:
with_injection = with_injector(injector)
@with_injection
def test_foo(session: Session):
# session is injected!
...
Yes, absolutely.
One more option which allows mixing pytest-injected and Injector-injected arguments.
def with_injection(f):
import functools
from injector import inject
import inspect
f = inject(f)
@functools.wraps(f)
def wrapper(**kwargs): # This function is inspected and called by pytest
return injector.call_with_injection(f, kwargs=kwargs)
# Remove parameters which will be provided by injector, so that pytest is not confused
sig = inspect.signature(wrapper)
not_injected_parameters = tuple(param for param in sig.parameters.values() if param.name not in f.__bindings__)
wrapper.__signature__ = sig.replace(parameters=not_injected_parameters)
return wrapper
How would one use this library with pytest without actually doing
It'd be nice if I could use the injector with pytest.
(I get that using a type annotation is probably not possible)
Does anyone have any nice patterns for this that don't break the pytest paradigm.