def test_make_will_inject_container(self): def returns_arg(c: Container): return c container = Container() arg = container.make(returns_arg) assert arg is container
def test_make_string_argument_works(self): def make_n(): return 5 container = Container() container.bind('n', make_n) assert container.make('n') == 5
def test_make_raises_if_no_annotation(self): class X: def __init__(self, foo): pass container = Container() with assert_raises(ResolutionError, 'foo'): container.make(X)
def test_make_passes_kwargs(self): class X: def __init__(self, foo): self.foo = foo container = Container() x = container.make(X, {'foo': 'bar'}) assert x.foo == 'bar'
def test_make_is_not_caching_instances(self): class X: pass container = Container() x1 = container.make(X) x2 = container.make(X) assert x1 is not x2
def test_make_handles_None_annotation(self): class X: def __init__(self, foo: None): self.foo = foo container = Container() x = container.make(X) assert x.foo is None
def test_make_raises_if_given_invalid_explicit_kwargs(self): class X: def __init__(self, foo: str): self.foo = foo container = Container() with assert_raises(ResolutionError, 'bar'): container.make(X, {'foo': 'x', 'bar': 'y'})
def test_make_raises_if_given_invalid_explicit_kwargs(self): class X: def __init__(self, foo: str): self.foo = foo container = Container() with assert_raises(ResolutionError, "bar"): container.make(X, {"foo": "x", "bar": "y"})
def test_make_passes_kwargs(self): class X: def __init__(self, foo): self.foo = foo container = Container() x = container.make(X, {"foo": "bar"}) assert x.foo == "bar"
def test_make_does_not_auto_create_namedtuple(self): class X: pass Y = namedtuple('Y', ['x']) container = Container() with assert_raises(ResolutionError, 'x'): container.make(Y)
def test_make_contextual_with_builtin_type(self): class X: def __init__(self, foo: str): self.foo = foo container = Container() container.bind_contextual(when=X, wants=str, called='foo', give=lambda: 'bar') x = container.make(X) assert x.foo == 'bar'
def test_make_with_singletons(self): class X: pass container = Container() container.bind(X, X, lifetime_strategy=SINGLETON) x1 = container.make(X) x2 = container.make(X) assert x1 is x2
def test_make_raises_if_not_concrete(self): class X(abc.ABC): @abc.abstractmethod def foo(self): pass container = Container() with assert_raises(ResolutionError, X): container.make(X)
def test_make_contextual_with_builtin_type(self): class X: def __init__(self, foo: str): self.foo = foo container = Container() container.bind_contextual(when=X, wants=str, wants_name="foo", give=lambda: "bar") x = container.make(X) assert x.foo == "bar"
def test_make_supports_namedtuple_contextual_binding(self): class X: pass Y = namedtuple("Y", ["x"]) container = Container() container.bind_contextual(when=Y, wants_name="x", give=X) y = container.make(Y) assert isinstance(y.x, X)
def test_make_injects_class_annotations(self): class X: pass class Y: foo: X container = Container() y = container.make(Y) assert isinstance(y.foo, X)
def test_make_supports_namedtuple_contextual_binding(self): class X: pass Y = namedtuple('Y', ['x']) container = Container() container.bind_contextual(when=Y, called='x', give=X) y = container.make(Y) assert isinstance(y.x, X)
def test_make_does_not_inject_class_annotations_if_hinted_as_classvar(self): class X: pass class Y: foo: ClassVar[X] container = Container() y = container.make(Y) assert not hasattr(Y, 'foo') assert not hasattr(y, 'foo')
def test_make_string_argument_in_subrequirement(self): class X: def __init__(self, arg: 'n'): # noqa: F821 self.arg = arg def make_n(): return 5 container = Container() container.bind('n', make_n) assert container.make(X).arg == 5
def test_make_init_kwargs_also_apply_to_attrs(self): class X: pass class Y: foo: X x1 = X() container = Container() y = container.make(Y, {'foo': x1}) assert not hasattr(Y, 'foo') assert y.foo is x1
def test_make_simple_autowiring_with_concrete_annotations(self): class X: pass class Y: def __init__(self, x: X): self.x = x container = Container() y = container.make(Y) assert isinstance(y, Y) assert isinstance(y.x, X)
def test_make_caching_fibonacci(self): """A more complete quasi-real-life test""" class KeyValueDatabase(abc.ABC): @abc.abstractmethod def get(self, key): pass @abc.abstractmethod def has(self, key): pass @abc.abstractmethod def set(self, key, value): pass class MemoryStore(KeyValueDatabase): def __init__(self, initial_data: dict): self.data = initial_data def set(self, key, value): self.data[key] = value def has(self, key): return key in self.data def get(self, key): return self.data[key] class CachingFibonacci: def __init__(self, cache: KeyValueDatabase): self.cache = cache def calculate(self, n: int): if n in {0, 1}: return n if self.cache.has(n): return self.cache.get(n) return self.calculate(n - 1) + self.calculate(n - 2) container = Container() with assert_raises(ResolutionError, KeyValueDatabase): container.make(CachingFibonacci) container.bind(KeyValueDatabase, MemoryStore) with assert_raises(ResolutionError, "initial_data"): container.make(CachingFibonacci) container.bind_contextual( when=MemoryStore, wants=dict, wants_name="initial_data", give=lambda: {} ) fib = container.make(CachingFibonacci) assert fib.calculate(6) == 8
def test_make_optional_param_injection(self): class X: pass default_x = X() class Y: def __init__(self, foo: X = default_x): self.foo = foo container = Container() y = container.make(Y) assert y.foo is default_x
def test_make_does_not_inject_class_annotations_if_set_on_class(self): class X: pass x1 = X() class Y: foo: X = x1 container = Container() y = container.make(Y) assert y.foo is x1 assert Y.foo is x1
def test_make_init_kwargs_params_used_before_attr(self): class X: pass class Y: foo: X def __init__(self, foo: X): self.init_foo = foo container = Container() y = container.make(Y) assert isinstance(y.init_foo, X) assert not hasattr(y, 'foo')
def test_make_does_not_save_singleton_if_explicit_init_kwargs_set(self): class X: def __init__(self, foo: str): self.foo = foo container = Container() container.bind(X, lambda: X('foo'), SINGLETON) x1 = container.make(X) x2 = container.make(X, {'foo': 'bar'}) x3 = container.make(X) assert x1.foo == 'foo' assert x3 is x1 assert x2.foo == 'bar' assert x2 is not x1
def test_make_optional_attr_injection(self): class X: pass default_x = X() class Y: foo: X def __init__(self): self.foo = default_x container = Container() y = container.make(Y) assert y.foo is default_x
def test_make_optional_param_contextual_binding_prioritized_over_default(self): class X: pass default_x = X() class Y: def __init__(self, foo: X = default_x): self.foo = foo container = Container() bound_x = X() container.bind_contextual(when=Y, wants=X, give=lambda: bound_x) y = container.make(Y) assert y.foo is not default_x assert y.foo is bound_x
def test_make_optional_param_injection_default_prioritized_over_explicit_binding(self): class X: pass default_x = X() class Y: def __init__(self, foo: X = default_x): self.foo = foo container = Container() bound_x = X() container.bind_instance(X, bound_x) y = container.make(Y) assert y.foo is default_x assert y.foo is not bound_x
def test_make_does_not_inject_class_annotations_if_hinted_in_init(self): class X1: pass class X2: pass class Y: foo: X1 def __init__(self, foo: X2): self.foo = foo container = Container() y = container.make(Y) assert isinstance(y.foo, X2)