Coverage for tests/container_test.py: 98%
64 statements
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-10 22:09 +0200
« prev ^ index » next coverage.py v7.4.4, created at 2024-04-10 22:09 +0200
1from __future__ import annotations
3from typing import Any
5import pytest
7from diy import Container, RuntimeContainer, Specification
8from diy.errors import UninstanciableTypeError
11class ConstructurWithoutSelf:
12 def __init__() -> None: # type: ignore[reportSelfClsParameterName]
13 pass
16def test_container_reports_uninstantiable_types() -> None:
17 container = RuntimeContainer()
19 with pytest.raises(UninstanciableTypeError):
20 container.resolve(ConstructurWithoutSelf)
23class NoConstructor:
24 pass
27class ConstructorWithOnlySelf:
28 def __init__(self) -> None:
29 super().__init__()
30 self.something = "foo"
33@pytest.mark.parametrize(
34 "abstract",
35 [
36 NoConstructor,
37 ConstructorWithOnlySelf,
38 ],
39)
40def test_container_can_instantiate_constructors_that_require_no_arguments(
41 abstract: type[Any],
42) -> None:
43 container = RuntimeContainer()
44 instance = container.resolve(abstract)
45 assert isinstance(instance, abstract)
48def test_container_can_instantiate_constructors_that_only_require_default_arguments() -> (
49 None
50):
51 class ConstructorWithOneDefaultArgument:
52 def __init__(self, name: str = "default name") -> None:
53 super().__init__()
54 self.name = name
56 container = RuntimeContainer()
57 instance = container.resolve(ConstructorWithOneDefaultArgument)
58 assert isinstance(instance, ConstructorWithOneDefaultArgument)
61def test_container_actually_resolves_the_default_arguments() -> None:
62 container = RuntimeContainer()
64 sentinel = object()
66 def my_function(my_argument: Any = sentinel):
67 return my_argument
69 result = container.call(my_function)
70 assert result == sentinel
73class ApiClient:
74 def __init__(self, token: str) -> None:
75 super().__init__()
76 self.token = token
79def test_container_can_instantiate_kwargs_only_constructors() -> None:
80 spec = Specification()
81 spec.builders.add(ApiClient, lambda: ApiClient("test"))
83 container = RuntimeContainer(spec)
84 instance = container.resolve(ApiClient)
85 assert isinstance(instance, ApiClient)
88class ImplicitlyResolvesApiClient:
89 def __init__(self, api: ApiClient) -> None:
90 super().__init__()
91 self.api = api
94def test_container_can_implicitly_resolve_argument_that_are_contained_in_the_spec() -> (
95 None
96):
97 spec = Specification()
98 spec.builders.add(ApiClient, lambda: ApiClient("test"))
100 container = RuntimeContainer(spec)
101 instance = container.resolve(ImplicitlyResolvesApiClient)
102 assert isinstance(instance, ImplicitlyResolvesApiClient)
105def test_it_can_inject_itself_via_protocols() -> None:
106 spec = Specification()
107 container = RuntimeContainer(spec)
109 # TODO: Not sure if adding to the spec _after_ handing it into a container
110 # should have an effect on the container. Could be prevented by deep-
111 # copying the spec in the container constructor, but for this
112 # specific use-case, it is convenient. Though I guess it feelds more
113 # proper to support injecting builder arguments from the container
114 # instead.
115 spec.builders.add(Container, lambda: container)
117 instance = container.resolve(Container)
118 assert instance == container