Pytest-django: 如何在会话夹具上使用 django_db 标记?

创建于 2017-09-14  ·  15评论  ·  资料来源: pytest-dev/pytest-django

我有一种情况,我需要加载由函数创建的巨大 db 固定装置。 所有 api 测试都需要该夹具。 所以我在conftest.py做了一个会话装置,它可以做到。 但问题是pytest抛出以下异常,即使我已标记django_db

E Failed: Database access not allowed, use the "django_db" mark to enable it.
下面是我的代码片段。

from permission.helpers import update_permissions

pytestmark = [
        pytest.mark.django_db(transaction = True),]

@pytest.fixture(scope="session", autouse = True)
def permission(request):
        load_time_consuming_db_fixture()
db-configuration enhancement

最有用的评论

这是我从 Django 的基于 UnitTest 类的测试到 pytest-django 的最大痛点——在 Django 中,我们使用setUpTestData运行一次昂贵的数据库操作(相当于会话范围的 pytest 固定装置),然后有一个狡猾的技巧在 setUp 中运行obj.refresh_from_db() setUp以刷新类引用。

即使我只是创建一个数据库模型实例,并在每个 TC 重新加载它,这几乎总是比在每个测试用例中创建要快。

如果可以的话,如果我们可以将 pytest-tipsi-django 中的会话范围的固定装置合并到上游,那就太好了; 花了一些时间才找到这个问题和解决方案。

所有15条评论

@ludbek我们也错过了这样的功能并为此功能创建了插件:
https://github.com/tipsi/pytest-tipsi-django (用法:https://github.com/tipsi/pytest-tipsi-django/blob/master/test_django_plugin/app/tests/test_transactions.py)
和这个结合:
https://github.com/tipsi/pytest-tipsi-testing
它使您能够嵌套事务和正确的执行/关闭顺序。

@cybergrind感谢您的回复。 我一定会检查出来,让你知道它是怎么回事。

这是我从 Django 的基于 UnitTest 类的测试到 pytest-django 的最大痛点——在 Django 中,我们使用setUpTestData运行一次昂贵的数据库操作(相当于会话范围的 pytest 固定装置),然后有一个狡猾的技巧在 setUp 中运行obj.refresh_from_db() setUp以刷新类引用。

即使我只是创建一个数据库模型实例,并在每个 TC 重新加载它,这几乎总是比在每个测试用例中创建要快。

如果可以的话,如果我们可以将 pytest-tipsi-django 中的会话范围的固定装置合并到上游,那就太好了; 花了一些时间才找到这个问题和解决方案。

@paultiplady

我不确定pytest-tipsi-djanjo的方法是否适合pytest的通常测试模型。 最明显的区别是对夹具的整理:目前pytest没有明确地完成具有更广泛范围的不必要的夹具。 因此,您需要以特定顺序显式完成事务,通常,这可能会导致非常不同的效果(当前pytest可能会保持固定装置处于活动状态,即使活动测试及其固定装置根本不需要它)。

我们不得不将项目中的测试更改为这种方法,因为有时我们需要测试一些大场景,并且我们已经用稍微更好的夹具替换了大型测试中现有的手动事务管理,但它仍然需要注意订单测试。

现在我只能看到一个解决方案:将某种常见问题解答放入文档中。

感谢@cybergrind 提供更多详细信息。 我已经对此进行了更多研究,但是今天已经没有时间了-这就是我必须要做的地方,我很感激对这种方法是否有用进行理智检查,因为我对pytest内部不太熟悉。

另外,我不明白您所说的“pytest 没有明确地完成具有更广泛范围的不必要的固定装置”是什么意思,您能否进一步扩展一下? 那是指终结者吗? 这可能会影响我在下面写的内容。

pytest-django 插件使用django_db标记,该标记在 plugin.py 中的 _django_db_marker 中处理(https://github.com/pytest-dev/pytest-django/blob/master/pytest_django/plugin.py #L375),调用函数范围的db固定装置(https://github.com/pytest-dev/pytest-django/blob/master/pytest_django/fixtures.py#L142)。 这个夹具实例化了一个 Django TestCase,并调用它的_pre_setup函数(并将_post_teardown入队)。

我可以看到几个选项:

我想知道我们是否可以扩展_django_db_marker以选择性地进行课堂或会话
范围设置也是如此,它基本上与db做同样的事情,但调用相当于cls.setUpTestData或在标记 kwargs 中传递的函数。

对于类作用域,我也假设会话作用域,预期的行为是之后回滚数据库,所以我们基本上需要以正确顺序触发的作用域固定装置,并且每个固定装置都设置自己的原子交易。 我相信这意味着这需要对db夹具进行修改,而不是在它旁边运行一个单独的夹具。

这样我们就可以正确触发任何指定的类/会话级设置,并且每个类/会话都会调用一次该设置。 我认为需要对标记本身进行修改,因为如果您只是设置一个手动触发django_db_blocker.unblock()的会话范围函数,这似乎发生在 django_db 标记设置第一个事务之后。

这可能看起来像这样(在 plugin.py _django_db_marker()中):

    if marker:
        if marker.session_db:
            getfixturevalue(request, 'db_session')

        if marker.class_db:
            getfixturevalue(request, 'db_class')

        validate_django_db(marker)
        if marker.transaction:
            getfixturevalue(request, 'transactional_db')
        else:
            getfixturevalue(request, 'db')

这是疯狂的谈话,还是这个线程值得进一步探索?

关于定稿: https ://github.com/tipsi/pytest-tipsi-testing/blob/master/tests/test_finalization.py

如果没有明确的最终确定,此测试将无法工作,与非功能级别的数据库固定装置相同。 这是关于 pytest 实现的,所以在 pytest-django 中没有什么可以修复它。

#388 和 #33 的重复项

谢谢,关闭。

33 已关闭,#388 没有任何有意义的讨论(与本次不同)。 关闭这个@blueyed似乎很奇怪,如果有什么我建议关闭#388 并使这个成为这个问题的规范票。

👍 谢谢!

我也非常感谢这个功能。

@mkokotovich
多少? 足以让自己发生吗? ;)

无论如何,这里的主要/基本问题(来自原始评论)已经是数据库在测试期间被重置,因此没有简单的方法来拥有像这样的会话范围固定装置。

不过可能有用的是:

@pytest.fixture(scope="session")
def django_db_setup(django_db_setup, django_db_blocker):
    with django_db_blocker.unblock():
        with transaction.atomic():  # XXX: could/should use `TestCase_enter_atomics` for multiple dbs
            load_time_consuming_db_fixture()
            yield

这个想法是将所有内容包装到一个额外的原子块中。 但是,这是未经测试的,您可能需要为此实际使用TransactionTestCase

@paultiplady
您的评论听起来不错 - 即值得进一步探索 AFAICS(另请参阅我之前的评论)。

@blueyed我们使用这种方法一年多(将所有内容包装到一个额外的原子块中)它工作得很好。
但是你不能仅仅插入这样的东西,因为 pytest 没有确定性,其中会话级别(以及其他高于测试)将被关闭,因此它需要跟踪数据库事务的依赖关系,无论它们是否需要彼此直接.
因此,您需要在下一次测试之前显式跟踪事务堆栈并关闭嵌套事务。 可以通过这种方式完成: https ://github.com/tipsi/pytest-tipsi-django/blob/master/pytest_tipsi_django/django_fixtures.py#L46

抱歉,我想澄清一下,因为我相信我遇到了同样的问题:

如果我想创建一个依赖于创建新数据库对象的夹具,我想我可以做到

@pytest.mark.django_db(transaction=True)
@pytest.fixture(scope="session")
def object():
    object = Object.create_object(params)
    yield object
    // or alternatively
    object = mixer.blend('myObject')
    yield object

但是,我在测试用例运行时收到以下错误: Database access not allowed, use the "django_db" mark, or the "db" or "transactional_db" fixtures to enable it.

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

tolomea picture tolomea  ·  4评论

ojake picture ojake  ·  6评论

mjk4 picture mjk4  ·  4评论

rodrigorodriguescosta picture rodrigorodriguescosta  ·  4评论

MRigal picture MRigal  ·  3评论