繁体   English   中英

如何使用数据库访问在 model 上编写干净的测试

[英]How to write clean tests on model with database access

我正在使用 SQLAlchemy + Ormar 并且我想编写干净的测试,因为它可以使用pytest-django编写:

import pytest
@pytest.mark.django_db
def test_user_count():
    assert User.objects.count() == 0

我正在使用 FastAPI 而根本没有使用 Django 所以无法使用上述装饰器。

如何使用上述数据库访问权限在 model 上编写干净的测试,但不使用 Django。 拥有 SQLAlchemy + Ormar 的基础设施会很棒,但更改 ORM 也是一种选择。

model 示例进行测试:

class User(ormar.Model):
    class Meta:
        metadata = metadata
        database = database

    id: int = ormar.BigInteger(primary_key=True)
    phone: str = ormar.String(max_length=100)
    account: str = ormar.String(max_length=100)

我认为这个讨论对你有用https://github.com/collerek/ormar/discussions/136

使用 autouse 固定装置应该可以帮助您:

# fixture
@pytest.fixture(autouse=True, scope="module")  # adjust your scope
def create_test_database():
    engine = sqlalchemy.create_engine(DATABASE_URL)
    metadata.drop_all(engine) # i like to drop also before - even if test crash in the middle we start clean
    metadata.create_all(engine)
    yield
    metadata.drop_all(engine)

# actual test - note to test async you need pytest-asyncio and mark test as asyncio
@pytest.mark.asyncio
async def test_actual_logic():
    async with database:  # <= note this is the same database that used in ormar Models
        ... (logic)

这是我在项目根目录中的独立脚本(笔记本)使用的, manage.py所在的位置;

import sys, os, django

# append your project to your path
sys.path.append("./<your-project>") 
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "<your-project>.settings")

os.environ["DJANGO_ALLOW_ASYNC_UNSAFE"] = "true"     # for notebooks only 
django.setup()

# import the model 
from listings.models import Listing

但是,应该注意的是 Django 带有它自己的单元测试。 看看这里 这将使您能够使用python3 manage.py test <your-test>运行测试。

这里发生了一些神奇的事情(但这在pytest中通常是正确的)。 @pytest.mark.django_db夹具只是标记测试,但它自己并没有做太多其他事情。 繁重的工作稍后发生在 pytest-django 内部,插件将过滤/扫描带有该标记的测试并向它们添加适当的固定装置。

我们可以复制这种行为:

# conftest.py (or inside a dedicated plugin, if you fancy)

import pytest

# register the custom marker called my_orm
def pytest_configure(config):
    config.addinivalue_line(
        "markers", "my_orm: This test uses my ORM to connect to my DB."
    )

@pytest.fixture()
def setup_my_orm():
    print("TODO: set up DB and connect.")
    
    yield

    print("TODO: tear down DB and disconnect.")


# this is where the magic happens
def pytest_runtest_setup(item):
    needs_my_orm = len([marker for marker in item.iter_markers(name="my_orm")]) > 0
    if needs_my_orm and "setup_my_orm" not in item.fixturenames:
        item.fixturenames.append("setup_my_orm")
# test_mymodule.py

@pytest.mark.my_orm
def test_foo():
    assert 0 == 0

您可以通过pytest -s检查测试是否确实打印了上述 TODO 语句。

当然,您可以使用标记的参数、更复杂的夹具范围等进一步自定义它。但是,这应该让您走上正确的轨道:)

暂无
暂无

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

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