繁体   English   中英

使用 TortoiseOrm 作为数据库层测试 FastApi 应用程序的问题

[英]Issues testing a FastApi app using TortoiseOrm as a database layer

我在使用 TortoiseOrm 作为数据库层测试 FastApi 应用程序时遇到问题。 请在下面找到一个最小的可重现示例。 该示例是一个带有 2 个端点的小型 API,它们接受get请求并返回虚拟数据。 一个端点与数据库交互,而另一个则不。

我的主要问题是,当我在浏览器或 Postman 上与它们交互时,虽然两个端点都能正常工作,但与数据库交互的端点在测试期间会引发错误。 请在下面找到完整的堆栈跟踪。 由于我难以理解的原因,似乎 TortoiseOrm 在测试期间无法建立数据库连接。

使用命令pytest运行测试

pytest -v -s -p no:warnings

主文件

from fastapi import FastAPI, status
from db import Tournament
from db import create_start_app_handler


def get_application():
    app = FastAPI()
    app.add_event_handler("startup", create_start_app_handler(app))
    return app


app = get_application()


@app.get("/",
    name="point1",
    status_code=status.HTTP_200_OK
)
async def home():
    return {
        "title":"Hello world"
    }


@app.get(
    "/save/",
    name="point2",
    status_code=status.HTTP_200_OK
)
async def save_data():
    await Tournament.create(
        name="test2"
    )
    return {
        "status":"created"
    }

数据库.py

from fastapi import FastAPI
from tortoise.models import Model
from tortoise import fields
from tortoise.contrib.fastapi import register_tortoise
from typing import Callable


class Tournament(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=255)


async def init_db(app: FastAPI):
    register_tortoise(
        app,
        db_url='postgres://admin:admin@localhost:5432/postgres_test',
        modules={"models": ["db"]},
        generate_schemas=True,
        add_exception_handlers=True,
    ) 


def create_start_app_handler(app: FastAPI) -> Callable:
    async def start_app() -> None:
        await init_db(app)
    return start_app

test_main.py

import pytest
from fastapi.testclient import TestClient
from main import app


class TestBase:
    client = TestClient(app)

    def test_home(self) -> None:
        response = self.client.get(
            app.url_path_for('point1')
        )
        assert response.status_code == 200

    def test_save(self) -> None:
        response = self.client.get(
            app.url_path_for('point2')
        )
        assert response.status_code == 200

堆栈跟踪

_____________________________________________________________________ TestBase.test_save ______________________________________________________________________

self = <test_main.TestBase object at 0x7fb7fb5831f0>

    def test_save(self) -> None:
>       response = self.client.get(
            app.url_path_for('point2')
        )

test_main.py:16: 
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
../venv/lib/python3.8/site-packages/requests/sessions.py:555: in get
    return self.request('GET', url, **kwargs)
../venv/lib/python3.8/site-packages/starlette/testclient.py:415: in request
    return super().request(
../venv/lib/python3.8/site-packages/requests/sessions.py:542: in request
    resp = self.send(prep, **send_kwargs)
../venv/lib/python3.8/site-packages/requests/sessions.py:655: in send
    r = adapter.send(request, **kwargs)
../venv/lib/python3.8/site-packages/starlette/testclient.py:243: in send
    raise exc from None
../venv/lib/python3.8/site-packages/starlette/testclient.py:240: in send
    loop.run_until_complete(self.app(scope, receive, send))
/home/unyime/anaconda3/lib/python3.8/asyncio/base_events.py:616: in run_until_complete
    return future.result()
../venv/lib/python3.8/site-packages/fastapi/applications.py:208: in __call__
    await super().__call__(scope, receive, send)
../venv/lib/python3.8/site-packages/starlette/applications.py:112: in __call__
    await self.middleware_stack(scope, receive, send)
../venv/lib/python3.8/site-packages/starlette/middleware/errors.py:181: in __call__
    raise exc from None
../venv/lib/python3.8/site-packages/starlette/middleware/errors.py:159: in __call__
    await self.app(scope, receive, _send)
../venv/lib/python3.8/site-packages/starlette/exceptions.py:82: in __call__
    raise exc from None
../venv/lib/python3.8/site-packages/starlette/exceptions.py:71: in __call__
    await self.app(scope, receive, sender)
../venv/lib/python3.8/site-packages/starlette/routing.py:580: in __call__
    await route.handle(scope, receive, send)
../venv/lib/python3.8/site-packages/starlette/routing.py:241: in handle
    await self.app(scope, receive, send)
../venv/lib/python3.8/site-packages/starlette/routing.py:52: in app
    response = await func(request)
../venv/lib/python3.8/site-packages/fastapi/routing.py:219: in app
    raw_response = await run_endpoint_function(
../venv/lib/python3.8/site-packages/fastapi/routing.py:152: in run_endpoint_function
    return await dependant.call(**values)
main.py:31: in save_data
    await Tournament.create(
../venv/lib/python3.8/site-packages/tortoise/models.py:1104: in create
    db = kwargs.get("using_db") or cls._choose_db(True)
../venv/lib/python3.8/site-packages/tortoise/models.py:1014: in _choose_db
    db = router.db_for_write(cls)
../venv/lib/python3.8/site-packages/tortoise/router.py:39: in db_for_write
    return self._db_route(model, "db_for_write")
../venv/lib/python3.8/site-packages/tortoise/router.py:31: in _db_route
    return get_connection(self._router_func(model, action))
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <tortoise.router.ConnectionRouter object at 0x7fb7fb6272e0>, model = <class 'db.Tournament'>, action = 'db_for_write'

    def _router_func(self, model: Type["Model"], action: str):
>       for r in self._routers:
E       TypeError: 'NoneType' object is not iterable

../venv/lib/python3.8/site-packages/tortoise/router.py:18: TypeError
=================================================================== short test summary info ===================================================================
FAILED test_main.py::TestBase::test_save - TypeError: 'NoneType' object is not iterable
================================================================= 1 failed, 1 passed in 2.53s =================================================================

我该如何解决? 你可以在这里找到 Github 上的完整代码。

我像往常一样迟到了,但是:

导致错误的原因:数据库模式尚未初始化。

在您的特定情况下:您通过 fastapi 启动事件初始化乌龟。 默认情况下,启动事件不会在测试中触发,因此不会配置 tortoise。 因此,您需要在测试函数中创建 TestClient,例如使用with TestClient(app) as client:

通过此更改,您在 repo 中的测试似乎通过了。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM