python-3.x FastAPI TestClient覆盖寿命函数

rlcwz9us  于 2023-10-21  发布在  Python
关注(0)|答案(2)|浏览(192)

在使用python dependency injector框架的更复杂的设置中,我使用FastAPI应用程序对象的lifetime函数来正确连接所有内容。
在测试时,我想用不同的版本(fakes)替换一些对象,在我看来,实现这一点的自然方法是重写或模仿应用对象的lifetime函数。但是我不知道我是否/如何做到这一点。
MRE如下

import pytest
from contextlib import asynccontextmanager
from fastapi.testclient import TestClient
from fastapi import FastAPI, Response, status

greeting = None

@asynccontextmanager
async def _lifespan(app: FastAPI):
    # Initialize dependency injection
    global greeting
    greeting = "Hello"
    yield

@asynccontextmanager
async def _lifespan_override(app: FastAPI):
    # Initialize dependency injection
    global greeting
    greeting = "Hi"
    yield

app = FastAPI(title="Test", lifespan=_lifespan)

@app.get("/")
async def root():
    return Response(status_code=status.HTTP_200_OK, content=greeting)

@pytest.fixture
def fake_client():
    with TestClient(app) as client:
        yield client

def test_override(fake_client):
    response = fake_client.get("/")
    assert response.text == "Hi"

因此,基本上在fake_client fixture中,我想将其更改为使用_lifespan_override而不是原始的_lifespan,从而使上面的虚拟测试用例通过
我希望像with TestClient(app, lifespan=_lifespan_override) as client:这样的东西可以工作,但不支持。有没有什么方法可以让我嘲笑它来得到我想要的行为?
(The如果在assert语句中将“Hi”替换为“Hello”,则上面的mre可以工作)
下面的pyproject.toml以及所需的依赖项

[tool.poetry]
name = "mre"
version = "0.1.0"
description = "mre"
authors = []

[tool.poetry.dependencies]
python = "^3.10"
fastapi = "^0.103.2"

[tool.poetry.group.dev.dependencies]
pytest = "^7.1.2"
httpx = "^0.25.0"

[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

编辑:尝试扩展我的代码与建议从哈米德Akhavan如下

@pytest.fixture
def fake_client():
    app.dependency_overrides[_lifespan] = _lifespan_override
    with TestClient(app) as client:
        yield client

但它不起作用,即使它看起来应该是正确的方法。有问题吗?

wfsdck30

wfsdck301#

根据FastAPI文档,我认为覆盖lifespan的正确方法是:

import app  # import your FastAPI app
app.dependency_overrides[lifespan] = _lifespan
6psbrbz9

6psbrbz92#

我找到了一个解决我的问题的方法,它不包括重写lifetime函数,所以不是上面问题的通用解决方案。
正如我提到的,我在真实的应用程序中的具体问题是使用python依赖注入器框架,它为它的容器提供了重写方法。因此,解决方案是在测试过程中连接依赖项时使用该重写功能,这意味着不需要触及lifetime函数
这里有一个完整的工作MRE的情况下,任何人都感兴趣。

import pytest
from contextlib import asynccontextmanager
from fastapi.testclient import TestClient
from fastapi import FastAPI, Response, status, Depends
from dependency_injector import containers, providers
from dependency_injector.wiring import Provide, inject

class HelloGreeter():
    def greet(self):
        return "Hello"

class Container(containers.DeclarativeContainer):
    greeter = providers.Singleton(HelloGreeter)

@asynccontextmanager
async def _lifespan(app: FastAPI):
    # Initialize dependency injection
    container = Container()
    container.wire(modules=[__name__])
    yield

app = FastAPI(title="Test", lifespan=_lifespan)

@app.get("/")
@inject
async def root(greeter=Depends(Provide[Container.greeter])):
    return Response(status_code=status.HTTP_200_OK, content=greeter.greet())

@pytest.fixture
def fake_client():
    class HiGreeter():
        def greet(self):
            return "Hi"
    with Container.greeter.override(HiGreeter()):
        with TestClient(app) as client:
            yield client

def test_override(fake_client):
    response = fake_client.get("/")
    assert response.text == "Hi"

相关问题