簡體   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