2

我正在将 pytest 与 Django 一起使用(通过pytest-django),我想知道是否有可能有一个范围固定装置class在数据库中创建一些模型,然后在每次测试结束时删除这些模型。

例如;

@pytest.fixture(scope='class')
def expensive():
    return MyModel.objects.create()


@pytest.mark.django_db()
class TestMyModel:

    def test_a(self, expensive):
          MyModel.objects.get()  # All good

    def test_b(self, expensive):
          MyModel.objects.get()  # raises MyModel.DoesNotExist             

这只是一个简化的示例,在我的实际代码中,夹具expensive实际上正在做一些需要一些时间的事情(我实际上正在使用参数化测试,但我想这不会有任何区别)。我希望夹具中创建的数据expensive一旦超出类的范围就会回滚,以免干扰其他测试。

似乎我正在尝试做的事情可能可以使用夹具django_db_blocker但是我无法让它按照我想要的方式运行。

4

1 回答 1

0

在过去的几天里,我一直在为同样的问题苦苦挣扎,并设法提出了一个几乎适用于所有情况的解决方案。它的一种基于pytest-django来源的黑客攻击。如果将其添加到测试类中,请确保不添加任何内置的pytest-djangodb 标记。

import psycopg2
from django.apps import apps
from django.test.utils import setup_databases, teardown_databases
# alternatively if you're not on django 1.11 you need
# from pytest_django.compat import setup_databases, teardown_databases

@pytest.fixture(scope='class')
def class_scoped_db(django_db_blocker):
    try:
        django_db_blocker.unblock()
        # if test db exists use it and delete all objects once done
        _ = psycopg2.connect(dbname='test_{your_db_name}')
        yield
        # drop all objects created once we are out of scope
        for app_name in {list of your django apps}:
            for model in apps.get_app_config(app_name).get_models():
                model.objects.all().delete()

    except psycopg2.OperationalError:
        # if test db doesn't exist then create one and tear down once done
        db_cfg = setup_databases(verbosity=pytest.config.option.verbose,
                                 interactive=False)
        yield
        teardown_databases(db_cfg, verbosity=pytest.config.option.verbose)

    finally:
        django_db_blocker.restore()

我发现它不起作用的一种情况是,当第一个运行需要数据库访问的测试使用时class_scoped_db,我认为 teardown 方法使数据库连接处于剩余测试无法使用的奇怪状态。在完整的测试运行或测试单个测试类或模块时对我来说效果很好

于 2017-08-01T17:07:50.223 回答