<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Tag: dependency-injection - blog.dotcs.me</title>
  <link href="https://blog.dotcs.me/feeds/tags/dependency-injection.xml" rel="self" type="application/atom+xml"/>
  <link href="https://blog.dotcs.me/tags/dependency-injection" rel="alternate" type="text/html"/>
  <updated>2024-12-18T18:34:21.216Z</updated>
  <author>
    <name>dotcs</name>
  </author>
  <id>https://blog.dotcs.me/feeds/tags/dependency-injection.xml</id>
  <entry>
    <title>Combining FastAPI Dependency Injection with Service and Repository Layers</title>
    <published>2024-11-11T21:50:16+01:00</published>
    <updated>2024-11-11T21:50:16+01:00</updated>
    <id>https://blog.dotcs.me/posts/fastapi-dependency-injection-x-layers</id>
    <link href="https://blog.dotcs.me/posts/fastapi-dependency-injection-x-layers"/>
    <summary>Learn how to integrate FastAPI's DI system across multiple layers of your application to build a robust and testable application.</summary>
    <content type="html" xml:base="https://blog.dotcs.me/posts/fastapi-dependency-injection-x-layers">&lt;p&gt;FastAPI has a wonderful Dependency Injection (DI) system that works reasonably well at the controller level.
However, to build a robust and testable application, it is helpful to make use of a dependency injection chain that spans multiple layers of the application.
This approach not only promotes a clean architecture but also significantly improves the test situation.&lt;/p&gt;
&lt;h2 id="starting-point"&gt;&lt;a class="markdownIt-Anchor" href="#starting-point"&gt;&lt;span class="hidden sm:inline-block x-headline-anchor"&gt;#&lt;/span&gt;&lt;/a&gt; Starting Point&lt;/h2&gt;
&lt;p&gt;Typically, a FastAPI application consists of a controller layer (the FastAPI endpoints), a service layer and often a repository layer.
While FastAPI provides a way to inject dependencies into the controller layer, it does not provide a way to inject dependencies into the service or repository layers.&lt;/p&gt;
&lt;p&gt;But this does not mean that we cannot use dependency injection across multiple layers.
By building a dependency injection chain that spans multiple layers, we can inject dependencies into the service and repository layers as well.&lt;/p&gt;
&lt;p&gt;Let's see how we can do this and get our hands dirty with some code.&lt;/p&gt;
&lt;p&gt;A typical application might look like this:&lt;/p&gt;
&lt;div class="mermaid"&gt;graph LR
    A[Controller] --&amp;gt; B[Service]
    B --&amp;gt; C[Repository]
    C --&amp;gt; D[Database]
&lt;/div&gt;&lt;p&gt;But often we have application settings that might be used across the application in all layers, which let the application look like this:&lt;/p&gt;
&lt;div class="mermaid"&gt;graph TD
subgraph Z[&amp;quot; &amp;quot;]
direction LR
    A[Controller] --&amp;gt; B[Service]
    B --&amp;gt; C[Repository]
    C --&amp;gt; D[Database]
end
    E[Settings]
    E --&amp;gt; A
    E --&amp;gt; B
    E --&amp;gt; C
&lt;/div&gt;&lt;p&gt;Sometimes we also have a logger that we want to inject into all layers:&lt;/p&gt;
&lt;div class="mermaid"&gt;graph TD
subgraph Z[&amp;quot; &amp;quot;]
direction LR
    A[Controller] --&amp;gt; B[Service]
    B --&amp;gt; C[Repository]
    C --&amp;gt; D[Database]
end
E[Settings]
E --&amp;gt; A
E --&amp;gt; B
E --&amp;gt; C
F[Logger]
F --&amp;gt; A
F --&amp;gt; B
F --&amp;gt; C
&lt;/div&gt;&lt;p&gt;And many more dependencies might be possible.
Some of them could be shared across all layers, some of them might be shared only between some layers, and some of them might be specific to a single layer.&lt;/p&gt;
&lt;h2 id="building-the-dependency-injection-chain"&gt;&lt;a class="markdownIt-Anchor" href="#building-the-dependency-injection-chain"&gt;&lt;span class="hidden sm:inline-block x-headline-anchor"&gt;#&lt;/span&gt;&lt;/a&gt; Building the Dependency Injection Chain&lt;/h2&gt;
&lt;p&gt;To build a dependency injection chain that spans multiple layers, we can use Python's &lt;code&gt;Annotated&lt;/code&gt; type and the &lt;code&gt;Depends&lt;/code&gt; class from FastAPI.&lt;/p&gt;
&lt;p&gt;Let's start by defining a &lt;code&gt;Settings&lt;/code&gt; class that holds our application settings by inheriting from Pydantic &lt;code&gt;BaseSettings&lt;/code&gt; class.
This helps to ensure that the settings are correctly typed and validated and can be easily injected into other classes.
&lt;em&gt;(Also it helps to load the settings from environment variables or configuration files, which is typically why I like to use it in my applications.)&lt;/em&gt; 😉&lt;/p&gt;
&lt;pre class="hljs"&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;# file: settings.py&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; pydantic &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; BaseSettings

&lt;span class="hljs-class"&gt;&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title"&gt;Settings&lt;/span&gt;(&lt;span class="hljs-params"&gt;BaseSettings&lt;/span&gt;):&lt;/span&gt;
    app_name: &lt;span class="hljs-built_in"&gt;str&lt;/span&gt; = &lt;span class="hljs-string"&gt;&amp;quot;My App&amp;quot;&lt;/span&gt;
    db_connection_string: &lt;span class="hljs-built_in"&gt;str&lt;/span&gt; = &lt;span class="hljs-string"&gt;&amp;quot;sqlite:///:memory:&amp;quot;&lt;/span&gt;
    debug: &lt;span class="hljs-built_in"&gt;bool&lt;/span&gt; = &lt;span class="hljs-literal"&gt;False&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, let us define the three layers of our application: the controller, service, and repository layers.&lt;/p&gt;
&lt;p&gt;The repository layer is responsible for fetching data from the database.
In this example, we will keep it simple and return some sample data.
In a real-world application, this would be a database query, often by using an ORM like SQLAlchemy.&lt;/p&gt;
&lt;pre class="hljs"&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;# file: repository.py&lt;/span&gt;
&lt;span class="hljs-class"&gt;&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title"&gt;Repository&lt;/span&gt;:&lt;/span&gt;
    &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;__init__&lt;/span&gt;(&lt;span class="hljs-params"&gt;self, settings: Settings, db: Database&lt;/span&gt;):&lt;/span&gt;
        self.settings = settings
        self.db = db   &lt;span class="hljs-comment"&gt;# just to show that we can inject multiple dependencies&lt;/span&gt;

    &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;get_sample_data&lt;/span&gt;(&lt;span class="hljs-params"&gt;self&lt;/span&gt;):&lt;/span&gt;
        &lt;span class="hljs-comment"&gt;# usually this would be a database query&lt;/span&gt;
        &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; [{&lt;span class="hljs-string"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;: &lt;span class="hljs-string"&gt;&amp;quot;Alice&amp;quot;&lt;/span&gt;}, {&lt;span class="hljs-string"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;: &lt;span class="hljs-string"&gt;&amp;quot;Bob&amp;quot;&lt;/span&gt;}]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that we have not used FastAPI's &lt;code&gt;Depends&lt;/code&gt; here.
This is because the repository layer is not controlled by FastAPI.
Instead we will inject the &lt;code&gt;Settings&lt;/code&gt; and &lt;code&gt;Database&lt;/code&gt; objects into the repository layer by using the normal dependency injection pattern, meaning we will pass them as arguments to the constructor.&lt;/p&gt;
&lt;p&gt;The service layer is responsible for business logic.
In this example, we will keep it simple and return the application name in uppercase.&lt;/p&gt;
&lt;pre class="hljs"&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;# file: service.py&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; fastapi &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; Depends

&lt;span class="hljs-class"&gt;&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title"&gt;Service&lt;/span&gt;:&lt;/span&gt;
    &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;__init__&lt;/span&gt;(&lt;span class="hljs-params"&gt;self, settings: Settings, repository: Repository&lt;/span&gt;):&lt;/span&gt;
        self.settings = settings
        self.repository = repository

    &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;app_name_upper&lt;/span&gt;(&lt;span class="hljs-params"&gt;self&lt;/span&gt;):&lt;/span&gt;
        &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; self.settings.app_name.upper()

    &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;get_sample_data_with_upper_names&lt;/span&gt;(&lt;span class="hljs-params"&gt;self&lt;/span&gt;):&lt;/span&gt;
        data = self.repository.get_sample_data()
        &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; [{&lt;span class="hljs-string"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;: item[&lt;span class="hljs-string"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;].upper()} &lt;span class="hljs-keyword"&gt;for&lt;/span&gt; item &lt;span class="hljs-keyword"&gt;in&lt;/span&gt; data]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before we define the controller layer, let us write the dependency injection functions which FastAPI will use to inject the dependencies into the service and repository layers.
These functions are needed by the FastAPI dependency injection system to resolve the dependencies.
Their purpose is to instantiate the objects.
&lt;a href="https://fastapi.tiangolo.com/tutorial/dependencies/#simple-and-powerful"&gt;&lt;i class="las la-external-link-alt" title="This link refers to an external site"&gt;&lt;/i&gt;FastAPI allows to let dependencies depend on other dependencies&lt;/a&gt;, which allows to build a dependency chain.&lt;/p&gt;
&lt;pre class="hljs"&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;# file: dependencies.py&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; typing &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; Annotated

&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; myapp.settings &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; Settings
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; myapp.database &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; Database
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; myapp.repository &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; Repository

&lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;get_settings&lt;/span&gt;():&lt;/span&gt;
    &lt;span class="hljs-string"&gt;&amp;quot;&amp;quot;&amp;quot;Returns the application settings.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; Settings()

&lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;get_db&lt;/span&gt;(&lt;span class="hljs-params"&gt;settings: Annotated[Settings, Depends(&lt;span class="hljs-params"&gt;get_settings&lt;/span&gt;)]&lt;/span&gt;):&lt;/span&gt;
    &lt;span class="hljs-string"&gt;&amp;quot;&amp;quot;&amp;quot;Returns the database connection.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; Database(connection_string=settings.db_connection_string).connect()

&lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;get_repository&lt;/span&gt;(&lt;span class="hljs-params"&gt;settings: Annotated[Settings, Depends(&lt;span class="hljs-params"&gt;get_settings&lt;/span&gt;)], db: Annotated[Database, Depends(&lt;span class="hljs-params"&gt;get_db&lt;/span&gt;)]&lt;/span&gt;):&lt;/span&gt;
    &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; Repository(settings, db)

&lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;get_service&lt;/span&gt;(&lt;span class="hljs-params"&gt;settings: Annotated[Settings, Depends(&lt;span class="hljs-params"&gt;get_settings&lt;/span&gt;), repository: Annotated[Repository, Depends(&lt;span class="hljs-params"&gt;get_repository&lt;/span&gt;)]&lt;/span&gt;):&lt;/span&gt;
    &lt;span class="hljs-string"&gt;&amp;quot;&amp;quot;&amp;quot;Returns the service layer.&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; Service(settings, repository)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, we define the controller layer, which consists of the FastAPI endpoints.
We inject the &lt;code&gt;Service&lt;/code&gt; class into the controller layer using the &lt;code&gt;Depends&lt;/code&gt; class from FastAPI.&lt;/p&gt;
&lt;p&gt;Note that we do not need to inject the &lt;code&gt;Repository&lt;/code&gt; class into the controller layer because it is already injected into the &lt;code&gt;Service&lt;/code&gt; class, which is then injected into the controller layer.
Also there is no need to retrieve and pass the database connection to the service, because the repository is responsible for that.&lt;/p&gt;
&lt;p&gt;FastAPI will automatically resolve the dependency chain and resolve the dependencies in the correct order.&lt;/p&gt;
&lt;pre class="hljs"&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;# app.py&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; fastapi &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; FastAPI
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; typing &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; Annotated

&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; myapp.dependencies &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; get_service

app = FastAPI()

&lt;span class="hljs-meta"&gt;@app.get(&amp;quot;/&amp;quot;)&lt;/span&gt;
&lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;read_root&lt;/span&gt;(&lt;span class="hljs-params"&gt;service: Annotated[Service, Depends(&lt;span class="hljs-params"&gt;get_service&lt;/span&gt;)]&lt;/span&gt;):&lt;/span&gt;
    &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; {&lt;span class="hljs-string"&gt;&amp;quot;app_name&amp;quot;&lt;/span&gt;: service.app_name_upper()}

&lt;span class="hljs-meta"&gt;@app.get(&amp;quot;/sample_data&amp;quot;)&lt;/span&gt;
&lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;read_sample_data&lt;/span&gt;(&lt;span class="hljs-params"&gt;service: Annotated[Service, Depends(&lt;span class="hljs-params"&gt;get_service&lt;/span&gt;)]&lt;/span&gt;):&lt;/span&gt;
    &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; service.get_sample_data_with_upper_names()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that from the controller layer, we only inject the &lt;code&gt;Service&lt;/code&gt; class.
If needed, we could also inject the &lt;code&gt;Settings&lt;/code&gt; class or any other dependencies that are meant to be used in all layers.
But there is no need to inject the &lt;code&gt;Repository&lt;/code&gt; class or the &lt;code&gt;Database&lt;/code&gt; class into the controller layer!&lt;/p&gt;
&lt;h2 id="testing"&gt;&lt;a class="markdownIt-Anchor" href="#testing"&gt;&lt;span class="hidden sm:inline-block x-headline-anchor"&gt;#&lt;/span&gt;&lt;/a&gt; Testing&lt;/h2&gt;
&lt;p&gt;This significantly simplifies testing.&lt;/p&gt;
&lt;p&gt;For example, to test the &lt;code&gt;Service&lt;/code&gt; class, we can easily mock the &lt;code&gt;Repository&lt;/code&gt; class and the &lt;code&gt;Settings&lt;/code&gt; class.
This is generally possible because of the dependency injection pattern we have used for initializing the &lt;code&gt;Service&lt;/code&gt; class.&lt;/p&gt;
&lt;pre class="hljs"&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;# test_service.py&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; myapp.service &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; Service
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; myapp.repository &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; Repository
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; myapp.settings &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; Settings

&lt;span class="hljs-class"&gt;&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title"&gt;MockSettings&lt;/span&gt;(&lt;span class="hljs-params"&gt;Settings&lt;/span&gt;):&lt;/span&gt;
    app_name = &lt;span class="hljs-string"&gt;&amp;quot;My App under test&amp;quot;&lt;/span&gt;

&lt;span class="hljs-class"&gt;&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title"&gt;MockRepository&lt;/span&gt;(&lt;span class="hljs-params"&gt;Repository&lt;/span&gt;):&lt;/span&gt;
    &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;get_sample_data&lt;/span&gt;(&lt;span class="hljs-params"&gt;self&lt;/span&gt;):&lt;/span&gt;
        &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; [{&lt;span class="hljs-string"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;: &lt;span class="hljs-string"&gt;&amp;quot;Foo&amp;quot;&lt;/span&gt;}, {&lt;span class="hljs-string"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;: &lt;span class="hljs-string"&gt;&amp;quot;Bar&amp;quot;&lt;/span&gt;}]

&lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;test_app_name_upper&lt;/span&gt;():&lt;/span&gt;
    service = Service(MockSettings(), MockRepository())
    &lt;span class="hljs-keyword"&gt;assert&lt;/span&gt; service.app_name_upper() == &lt;span class="hljs-string"&gt;&amp;quot;MY APP UNDER TEST&amp;quot;&lt;/span&gt;

&lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;test_get_sample_data_with_upper_names&lt;/span&gt;():&lt;/span&gt;
    service = Service(MockSettings(), MockRepository())
    &lt;span class="hljs-keyword"&gt;assert&lt;/span&gt; service.get_sample_data_with_upper_names() == [{&lt;span class="hljs-string"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;: &lt;span class="hljs-string"&gt;&amp;quot;FOO&amp;quot;&lt;/span&gt;}, {&lt;span class="hljs-string"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;: &lt;span class="hljs-string"&gt;&amp;quot;BAR&amp;quot;&lt;/span&gt;}]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But the real power of this approach becomes apparent when testing the controller layer.&lt;/p&gt;
&lt;p&gt;Because we have used the &lt;code&gt;Depends&lt;/code&gt; class from FastAPI to inject the &lt;code&gt;Service&lt;/code&gt; class into the controller layer and have configured everything as a dependency chain, we can easily override the dependency injection functions to inject mock objects at any level.&lt;/p&gt;
&lt;p&gt;The following example shows how to override the &lt;code&gt;get_repository&lt;/code&gt; function to inject a &lt;code&gt;MockRepository&lt;/code&gt; object into the controller layer.&lt;/p&gt;
&lt;pre class="hljs"&gt;&lt;code&gt;&lt;span class="hljs-comment"&gt;# test_controller.py&lt;/span&gt;
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; fastapi.testclient &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; TestClient

&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; myapp.app &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; app
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; myapp.repository &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; Repository
&lt;span class="hljs-keyword"&gt;from&lt;/span&gt; myapp.dependencies &lt;span class="hljs-keyword"&gt;import&lt;/span&gt; get_settings, get_repository

&lt;span class="hljs-class"&gt;&lt;span class="hljs-keyword"&gt;class&lt;/span&gt; &lt;span class="hljs-title"&gt;MockRepository&lt;/span&gt;(&lt;span class="hljs-params"&gt;Repository&lt;/span&gt;):&lt;/span&gt;
    &lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;get_sample_data&lt;/span&gt;(&lt;span class="hljs-params"&gt;self&lt;/span&gt;):&lt;/span&gt;
        &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; [{&lt;span class="hljs-string"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;: &lt;span class="hljs-string"&gt;&amp;quot;Foo&amp;quot;&lt;/span&gt;}, {&lt;span class="hljs-string"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;: &lt;span class="hljs-string"&gt;&amp;quot;Bar&amp;quot;&lt;/span&gt;}]

&lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;mock_get_repository&lt;/span&gt;(&lt;span class="hljs-params"&gt;settings: Annotations[Settings, Depends(&lt;span class="hljs-params"&gt;get_settings&lt;/span&gt;)]&lt;/span&gt;):&lt;/span&gt;
    &lt;span class="hljs-comment"&gt;# Return a mock repository instead of the real one.&lt;/span&gt;
    &lt;span class="hljs-comment"&gt;# Also, we don&amp;#x27;t need the database connection here, so we can pass None.&lt;/span&gt;
    &lt;span class="hljs-keyword"&gt;return&lt;/span&gt; MockRepository(settings, &lt;span class="hljs-literal"&gt;None&lt;/span&gt;)

&lt;span class="hljs-function"&gt;&lt;span class="hljs-keyword"&gt;def&lt;/span&gt; &lt;span class="hljs-title"&gt;test_read_root&lt;/span&gt;():&lt;/span&gt;
    &lt;span class="hljs-comment"&gt;# Arrange&lt;/span&gt;
    &lt;span class="hljs-comment"&gt;# Override any dependencies that need to be mocked&lt;/span&gt;
    app.dependency_overrides[get_repository] = mock_get_repository

    client = TestClient(app)

    &lt;span class="hljs-comment"&gt;# Act&lt;/span&gt;
    response = client.get(&lt;span class="hljs-string"&gt;&amp;quot;/sample_data&amp;quot;&lt;/span&gt;)

    &lt;span class="hljs-comment"&gt;# Assert&lt;/span&gt;
    &lt;span class="hljs-keyword"&gt;assert&lt;/span&gt; response.status_code == &lt;span class="hljs-number"&gt;200&lt;/span&gt;
    &lt;span class="hljs-keyword"&gt;assert&lt;/span&gt; response.json() == [{&lt;span class="hljs-string"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;: &lt;span class="hljs-string"&gt;&amp;quot;FOO&amp;quot;&lt;/span&gt;}, {&lt;span class="hljs-string"&gt;&amp;quot;name&amp;quot;&lt;/span&gt;: &lt;span class="hljs-string"&gt;&amp;quot;BAR&amp;quot;&lt;/span&gt;}]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This approach allows us to easily test the controller layer by injecting mock objects at any level of the dependency chain.&lt;/p&gt;
&lt;p&gt;Note that we only need to override the dependency injection functions that we want to mock.
This makes the tests more focused and easier to understand.
In this case the full application logic can be tested without the need to start a real database or to mock the database connection by just mocking the repository layer.&lt;/p&gt;
&lt;h2 id="vizualizing-the-dependency-injection-chain"&gt;&lt;a class="markdownIt-Anchor" href="#vizualizing-the-dependency-injection-chain"&gt;&lt;span class="hidden sm:inline-block x-headline-anchor"&gt;#&lt;/span&gt;&lt;/a&gt; Vizualizing the Dependency Injection Chain&lt;/h2&gt;
&lt;p&gt;If the application is built like this and gets more complex over time because of many dependencies, it can be helpful to visualize the dependency injection chain.
Unfortunately I was not able to find a tool that can automatically generate a diagram from the code, so I wrote a Python library that can do this and published it on PyPI as an open-source project.&lt;/p&gt;
&lt;p&gt;The library works by inspecting the application code and generating a graph in the DOT language, which can then be rendered to an image using Graphviz.
Alternatively the library provides to output mermaid diagrams, which can be used in markdown files, e.g., on GitHub.&lt;/p&gt;
&lt;p&gt;The library is called &lt;a href="https://pypi.org/project/fastapi-di-viz/"&gt;&lt;i class="las la-external-link-alt" title="This link refers to an external site"&gt;&lt;/i&gt;fastapi-di-viz&lt;/a&gt; and can be installed into a project with &lt;code&gt;pip install fastapi-di-viz&lt;/code&gt;.
It provides a CLI tool that allows to inspect any FastAPI application.
See the &lt;a href="https://github.com/dotcs/fastapi-di-viz/"&gt;&lt;i class="lab la-github" title="This link refers to an external site"&gt;&lt;/i&gt;README&lt;/a&gt; for more details on how this works.&lt;/p&gt;
</content>
    <author>
      <name>dotcs</name>
    </author>
    <category term="architecture"/>
    <category term="dependency-injection"/>
    <category term="fastapi"/>
    <category term="python"/>
    <category term="tech"/>
    <category term="testing"/>
  </entry>
</feed>