在 Django 项目中,我们偶尔会遇到非托管模型,即元选项中没有 Managed = True 的模型。这些模型可能会使测试变得棘手,特别是当您的测试设置涉及托管和非托管模型或多个数据库的混合时(例如,一个包含托管模型,另一个包含非托管模型)。
这篇博文探讨了使用 pytest-django 测试非托管模型的方法,重点介绍了优点、缺点和解决方法,以帮助您有效管理这些场景。
在测试期间处理非托管模型的一种直接方法是将它们暂时标记为托管。具体方法如下:
# Add this to conftest.py @pytest.hookimpl(tryfirst=True) def pytest_runtestloop(): from django.apps import apps unmanaged_models = [] for app in apps.get_app_configs(): unmanaged_models += [m for m in app.get_models() if not m._meta.managed] for m in unmanaged_models: m._meta.managed = True
注意:要使此方法发挥作用,您需要在 pytest 设置(或 pytest.ini)中添加 --no-migrations 选项
参考:Stack Overflow
优点:
缺点:
或者,您可以在测试设置期间手动创建非托管模型。这种方法可确保迁移经过测试:
@pytest.fixture(scope="session", autouse=True) def django_db_setup(django_db_blocker, django_db_setup): with django_db_blocker.unblock(): for _connection in connections.all(): with _connection.schema_editor() as schema_editor: setup_unmanaged_models(_connection, schema_editor) yield def setup_unmanaged_models(connection, schema_editor): from django.apps import apps unmanaged_models = [ model for model in apps.get_models() if model._meta.managed is False ] for model in unmanaged_models: if model._meta.db_table in connection.introspection.table_names(): schema_editor.delete_model(model) schema_editor.create_model(model)
优点:
缺点:
Pytest-django 提供了数据库固定装置:django_db 和 django_db(transaction=True)。它们的区别如下:
django_db: 在测试用例结束时回滚更改,这意味着不会对数据库进行实际提交。
django_db(transaction=True): 在每个测试用例后提交更改并截断数据库表。由于每次测试后只有托管模型会被截断,这就是非托管模型在事务测试期间需要特殊处理的原因。
示例测试用例
@pytest.mark.django_db def test_example(): # Test case logic here pass @pytest.mark.django_db(transaction=True) def test_transactional_example(): # Test case logic here pass
由于事务测试仅截断托管模型,因此我们可以在测试运行期间将非托管模型修改为托管模型。这确保它们包含在截断中:
# Add this to conftest.py @pytest.hookimpl(tryfirst=True) def pytest_runtestloop(): from django.apps import apps unmanaged_models = [] for app in apps.get_app_configs(): unmanaged_models += [m for m in app.get_models() if not m._meta.managed] for m in unmanaged_models: m._meta.managed = True
在涉及 on_commit 挂钩的场景中,您可以通过直接捕获和执行 on_commit 回调来避免使用事务测试,使用 pytest-django(>= v.4.4) 中的固定装置 django_capture_on_commit_callbacks:
@pytest.fixture(scope="session", autouse=True) def django_db_setup(django_db_blocker, django_db_setup): with django_db_blocker.unblock(): for _connection in connections.all(): with _connection.schema_editor() as schema_editor: setup_unmanaged_models(_connection, schema_editor) yield def setup_unmanaged_models(connection, schema_editor): from django.apps import apps unmanaged_models = [ model for model in apps.get_models() if model._meta.managed is False ] for model in unmanaged_models: if model._meta.db_table in connection.introspection.table_names(): schema_editor.delete_model(model) schema_editor.create_model(model)
您还有其他处理非托管模型的方法或技巧吗?在下面的评论中分享它们!
以上是在 Pytest-Django 中处理非托管模型的详细内容。更多信息请关注PHP中文网其他相关文章!