Skip to content

Commit

Permalink
docs: testing section and override features (#54)
Browse files Browse the repository at this point in the history
* Create testing section and description for override features

* Add sphinx-copybutton package

---------

Co-authored-by: ivan <ivan.belyaev@ailet.com>
  • Loading branch information
nightblure and ivan authored Jul 16, 2024
1 parent 548b5e7 commit 17a245c
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

# -- General configuration
extensions = [
"sphinx_copybutton",
"sphinx.ext.duration",
"sphinx.ext.doctest",
"sphinx.ext.autodoc",
Expand Down
6 changes: 6 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@
providers/collections
providers/selector
providers/object
.. toctree::
:maxdepth: 1
:caption: Testing
testing/provider-overriding
.. toctree::
:maxdepth: 1
Expand Down
1 change: 1 addition & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
sphinx==7.*
sphinx-rtd-theme==2.*
myst-parser
sphinx-copybutton
107 changes: 107 additions & 0 deletions docs/testing/provider-overriding.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Provider overriding

DI container provides, in addition to direct dependency injection, another very important functionality:
**dependencies or providers overriding**.

Any provider registered with the container can be overridden.
This can help you replace objects with simple stubs, or with other objects.
**Override affects all providers that use the overridden provider (_see example_)**.

## Example

```python
from pydantic_settings import BaseSettings
from sqlalchemy import create_engine, Engine, text
from testcontainers.postgres import PostgresContainer
from that_depends import BaseContainer, providers, Provide, inject


class SomeSQLADao:
def __init__(self, *, sqla_engine: Engine):
self.engine = sqla_engine
self._connection = None

def __enter__(self):
self._connection = self.engine.connect()
return self

def __exit__(self, exc_type, exc_val, exc_tb):
self._connection.close()

def exec_query(self, query: str):
return self._connection.execute(text(query))


class Settings(BaseSettings):
db_url: str = 'some_production_db_url'


class DIContainer(BaseContainer):
settings = providers.Singleton(Settings)
sqla_engine = providers.Singleton(create_engine, settings.db_url)
some_sqla_dao = providers.Factory(SomeSQLADao, sqla_engine=sqla_engine)


@inject
def exec_query_example(some_sqla_dao=Provide[DIContainer.some_sqla_dao]):
with some_sqla_dao:
result = some_sqla_dao.exec_query('SELECT 234')

return next(result)


def main():
pg_container = PostgresContainer(image='postgres:alpine3.19')
pg_container.start()
db_url = pg_container.get_connection_url()

"""
We override only settings, but this override will also affect the 'sqla_engine'
and 'some_sqla_dao' providers because the 'settings' provider is used by them!
"""
local_testing_settings = Settings(db_url=db_url)
DIContainer.settings.override(local_testing_settings)

try:
result = exec_query_example()
assert result == (234,)
finally:
DIContainer.settings.reset_override()
pg_container.stop()


if __name__ == '__main__':
main()

```

The example above shows how overriding a nested provider ('_settings_')
affects another provider ('_engine_' and '_some_sqla_dao_').

## Override multiple providers

The example above looked at overriding only one settings provider,
but the container also provides the ability to override
multiple providers at once with method ```override_providers```.

The code above could remain the same except that
the single provider override could be replaced with the following code:

```python
def main():
pg_container = PostgresContainer(image='postgres:alpine3.19')
pg_container.start()
db_url = pg_container.get_connection_url()

local_testing_settings = Settings(db_url=db_url)
providers_for_overriding = {
'settings': local_testing_settings,
# more values...
}
with DIContainer.override_providers(providers_for_overriding):
try:
result = exec_query_example()
assert result == (234,)
finally:
pg_container.stop()
```

0 comments on commit 17a245c

Please sign in to comment.