pytest-django数据库初始化似乎没有重新加载数据库

时间:2016-11-11 01:59:36

标签: pytest django-unittest pytest-django

我们将情况简化为以下情况:

import pytest
from django.core.management import call_command
from foo import bar

@pytest.fixture(scope='session')
def django_db_setup(django_db_setup, django_db_blocker):
    LOGGER.info('ran call_command')
    with django_db_blocker.unblock():
        call_command('loaddata', 'XXX.json')

@pytest.mark.django_db(transaction=True)
def test_t1():
    assert len(bar.objects.all())

@pytest.mark.django_db(transaction=True)
def test_t2():
    assert len(bar.objects.all())

测试夹具XXX.json包括一个条。第一次测试(test_t1)成功。第二个测试(test_t2)失败。看来transaction = True属性不会导致数据库被测试夹具中的数据重新初始化。

如果使用来自unittest的TransactionTestCase,则初始化在类中的每个测试用例之前发生,并且所有测试都成功。

from django.test import TransactionTestCase

from foo import bar

class TestOne(TransactionTestCase):

    fixtures = ['XXX.json']

    def test_tc1(self):
        assert len(bar.objects.all())

    def test_tc2(self):
        assert len(bar.objects.all())
        objs = bar.objects.all()
        for bar in objs:
            bar.delete()

    def test_tc3(self):
        assert len(bar.objects.all())

我很感激为什么pytest示例不会导致第二个测试用例的重新初始化数据库的任何观点。

1 个答案:

答案 0 :(得分:0)

django_db_setup的作用域是会话,因此仅在测试会话开始时运行一次。使用transaction=True时,测试后数据库将刷新,因此django_db_setup中添加的所有数据都将被删除。

TransactionTestCase显然知道它正在使用事务,因此知道它需要为每个测试重新添加固定装置。

您有以下选择:

  1. 使用范围较小的灯具,如注释中所建议的,可能使用function范围。但这可能是选择加入的,它将在事务内运行,因此在测试完成后将被删除。
  2. 编写一个智能的固定装置,并通过检测测试何时使用事务来知道何时需要重新填充该数据。但是您需要确保在事务中使用的数据库连接不是 。我已经在django 1.11上做到了这一点,并且工作正常,尽管升级后可能需要修复。看起来像这样:
from unittest.mock import patch

from django.core.management import call_command
from django.db import DEFAULT_DB_ALIAS, ConnectionHandler

import pytest


_need_data_load = True


@pytest.fixture(autouse=True)
def auto_loaddata(django_db_blocker, request):
    global _need_data_load
    if _need_data_load:
        # Use a separate DB connection to ensure we're not in a transaction.
        con_h = ConnectionHandler()
        try:
            def_con = con_h[DEFAULT_DB_ALIAS]
            # we still need to unblock the database because that's a test level
            # constraint which simply monkey patches the database access methods
            # in django to prevent access.
            # 
            # Also note here we need to use the correct connection object
            # rather than any default, and so I'm assuming the command
            # imports `from django.db import connection` so I can swap it.
            with django_db_blocker.unblock(), patch(
                'path.to.your.command.modules.connection', def_con
            ):
                call_command('loaddata')
        finally:
            con_h.close_all()
        _need_auto_sql = False

    using_transactional_db = (
        'transactional_db' in request.fixturenames
        or 'live_server' in request.fixturenames
    )
    if using_transactional_db:
        # if we're using a transactional db then we will dump the whole thing
        # on teardown, so need to flag that we should set it up again after.
        _need_data_load = True