[英]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.