我正在学习 TDD fastapi、docker 和 pytest 课程的第一部分。我遇到了一个奇怪的问题,需要您的帮助。
当我创建第一个使用 torotoise 的测试时,它工作正常,将记录添加到数据库,并从 fastapi 获取它,没有任何问题。
分钟我添加另一个测试(添加的测试,然后读取记录),我收到此错误:
tortoise.exceptions.OperationalError: relation "textsummery" does not exist
(请忽略“摘要”一词中的拼写错误,它一开始是一个错误,但我用它作为强迫自己的一种方式不要盲目地复制粘贴所有内容)
如果我删除第一个测试,这个错误现在就消失了,并且测试顺利进行
为什么它不只使用已经创建的表?
代码:
# C:\src\tdd-fastapi\project\test\conftest.py
@pytest.fixture(scope="module")
def test_app_with_db():
# set up
app = create_applications()
app.dependency_overrides[get_settings] = get_settings_override
register_tortoise(
app,
db_url=os.environ.get("DATABASE_TEST_URL"),
modules={"models": ["app.models.tortoise"]},
generate_schemas=True,
add_exception_handlers=True,
)
和
# C:\src\tdd-fastapi\project\test\test_summeries.py
def test_create_summery(test_app_with_db):
response = test_app_with_db.post("/summeries/", data=json.dumps({"url": "https://foo.bar"}))
assert response.status_code == 201
assert response.json()["url"] == "https://foo.bar"
assert response.json()["id"] != 0
# ...
def test_read_summery(test_app_with_db):
response = test_app_with_db.post("/summeries/", data=json.dumps({"url": "https://foo.bar"}))
assert response.status_code == 201
assert response.json()["url"] == "https://foo.bar"
assert response.json()["id"] != 0
summery_id = response.json()["id"]
response = test_app_with_db.get(f"/summeries/{summery_id}/")
assert response.status_code == 200
response_dict = response.json()
assert response_dict["id"] == summery_id
assert response_dict["url"] == "https://foo.bar"
assert response_dict["summery"]
assert response_dict["created_at"]
根据评论建议,我添加了 torotoise 和 testconf
from tortoise import fields, models
from tortoise.contrib.pydantic import pydantic_model_creator
# C:\src\tdd-fastapi\project\app\models\tortoise.py
class TextSummery(models.Model):
url = fields.TextField()
summery = fields.TextField()
created_at = fields.DatetimeField(auto_now_add=True)
updated_at = fields.DatetimeField(auto_now=True)
def __str__(self):
return self.url
SummerySchema = pydantic_model_creator(TextSummery)
和
# C:\src\tdd-fastapi\project\test\conftest.py
import os
import pytest
from starlette.testclient import TestClient
from app.main import create_applications
from app.config import get_settings, Settings
from tortoise.contrib.fastapi import register_tortoise
def get_settings_override() -> Settings:
return Settings(testing=1, database_url=os.environ.get('DATABASE_TEST_URL'))
@pytest.fixture(scope='module')
def test_app():
# setup
app = create_applications()
app.dependency_overrides[get_settings] = get_settings_override
with TestClient(app) as test_client:
yield test_client
@pytest.fixture(scope="module")
def test_app_with_db():
# set up
app = create_applications()
app.dependency_overrides[get_settings] = get_settings_override
register_tortoise(
app,
db_url=os.environ.get("DATABASE_TEST_URL"),
modules={"models": ["app.models.tortoise"]},
generate_schemas=True,
add_exception_handlers=True,
)
with TestClient(app) as test_client:
# testing
yield test_client
# tear down
这个错误“tortoise.exceptions.OperationalError: relation "textsummery" does not exist”通常在你试图使用 Tortoise ORM 与你的数据库交互但数据库中还没有创建该表时出现。这在你运行测试时很常见,尤其是当你没有在测试之间正确设置和拆除你的数据库时。
以下是你遇到的问题的分步说明以及如何解决:
问题:
- 测试隔离: 你的测试应该彼此隔离,这意味着一个测试不应依赖于另一个测试的结果或状态。在你这个例子中,看起来你的第二个测试“test_read_summery”依赖于第一个测试“test_create_summery”创建的“textsummery”表。
- 数据库设置和拆除: 为了确保测试隔离,你需要为每个测试设置一个干净的数据库。这意味着你需要在每个测试之前创建“textsummery”表,并在测试之后将其删除。
解决方案:
你可以使用 Tortoise ORM 的测试实用程序来简化此过程。以下是如何修改你的
conftest.py
文件来为每个测试设置和拆除你的数据库:
import os
import pytest
from tortoise.contrib.fastapi import register_tortoise
from tortoise.contrib.test import finalizer, initializer
from starlette.testclient import TestClient
from app.main import create_applications # 替换成你实际的应用导入
from app.config import get_settings, Settings
from app.models.tortoise import TextSummery # 替换成你实际的模型导入
def get_settings_override() -> Settings:
return Settings(testing=1, database_url=os.environ.get('DATABASE_TEST_URL'))
@pytest.fixture(scope='module')
async def test_app_with_db():
"""使用数据库的测试应用程序的固定装置。"""
app = create_applications()
app.dependency_overrides[get_settings] = get_settings_override
# 从环境变量中获取测试数据库 URL
db_url = os.environ.get("DATABASE_TEST_URL")
# 使用测试数据库 URL 初始化 Tortoise ORM
initializer(["app.models.tortoise"], db_url=db_url)
# 为测试应用程序注册 Tortoise ORM
register_tortoise(
app,
db_url=db_url,
modules={"models": ["app.models.tortoise"]},
generate_schemas=True,
add_exception_handlers=True,
)
# 生成数据库架构
await Tortoise.generate_schemas()
with TestClient(app) as test_client:
# 在这里屈服测试客户端以运行测试
yield test_client
# 在每个测试之后清理数据库
await Tortoise._drop_databases()
finalizer()
解释:
-
@pytest.fixture(scope="module")
: 这确保了数据库设置和拆除在每个测试模块(文件)中只执行一次,从而使你的测试运行更快。 -
initializer(["app.models.tortoise"], db_url=db_url)
: 这将使用你的测试数据库 URL 初始化 Tortoise ORM 并加载你的模型。确保将"app.models.tortoise"
替换成你的模型模块的实际路径。 -
await Tortoise.generate_schemas()
: 这会在你的测试数据库中创建表。 -
yield test_client
: 这将测试客户端传递给你的测试函数。 -
await Tortoise._drop_databases()
: 这会在每个测试模块之后删除数据库,确保一个干净的状态。 -
finalizer()
: 这会清理 Tortoise ORM。
如何使用它:
只需将
test_app_with_db
固定装置传递给你的测试函数,就像这样:
def test_create_summery(test_app_with_db):
# ...你的测试代码...
def test_read_summery(test_app_with_db):
# ...你的测试代码...
这将确保在每个测试之前创建数据库并在之后将其删除,从而防止出现“关系不存在”错误并使你的测试更加可靠。
请记住将占位符值(如数据库 URL 和模型路径)替换成你的实际配置。
标签:python,fastapi,tortoise-orm From: 70138399