Pytest-django: Как использовать метку django_db в фиксации сеанса?

Созданный на 14 сент. 2017  ·  15Комментарии  ·  Источник: pytest-dev/pytest-django

У меня есть ситуация, когда мне нужно загрузить огромную базу данных, созданную функцией. Приспособление необходимо во всех тестах 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 с областью сеанса), а затем есть хитрый трюк, чтобы запустить 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-типси-тестирование
Это дает вам возможность вкладывать транзакции и корректировать порядок выполнения/завершения.

@cybergrind Спасибо за ответ. Я обязательно проверю и сообщу, как все прошло.

Это было самой большой проблемой для меня при переходе от тестов Django на основе классов UnitTest к pytest-django — в Django мы используем setUpTestData для однократного запуска дорогостоящих операций БД (эквивалентно фикстурам pytest с областью сеанса), а затем есть хитрый трюк, чтобы запустить obj.refresh_from_db() в setUp , чтобы обновить ссылки на классы.

Даже если я просто создаю один экземпляр модели БД и перезагружаю его каждый TC, это почти всегда быстрее, чем создание в каждом тестовом примере.

Было бы здорово, если бы мы могли объединить фикстуры уровня сеанса из pytest-tipsi-django вверх по течению, если это возможно; потребовалось немного копания, чтобы найти эту проблему и решение.

привет @paultiplady

Я не уверен, что подход от pytest-tipsi-djanjo подходит к обычной модели тестирования для pytest . Наиболее заметным отличием является финишная обработка фикстур: в настоящее время pytest явно не завершает ненужные фикстуры с более широким охватом. Таким образом, вам нужно явно завершать транзакции в определенном порядке, и в целом это может вызвать очень разные эффекты (в настоящее время pytest может держать фикстуры активными, даже если активный тест и его фикстуры вообще не требуют этого).

Нам пришлось изменить тесты в нашем проекте на этот подход, потому что нам иногда нужно тестировать некоторые большие сценарии, и мы заменили существующее ручное управление транзакциями в огромных тестах немного лучшими фикстурами, но это все еще требует внимания к тестам заказов.

На данный момент я вижу только одно решение для этого: внести какой-то FAQ в документацию.

Спасибо за дополнительную информацию @cybergrind. Я копался в этом немного больше, но на сегодня у меня не хватило времени - вот где я должен, я был бы признателен за проверку работоспособности на предмет того, полезен ли этот подход или нет, поскольку я не так хорошо знаком с внутренностями pytest.

Также я не понимаю, что вы подразумеваете под «pytest явно не завершает ненужные приспособления с более широкой областью действия», не могли бы вы рассказать об этом немного подробнее, пожалуйста? Это относится к финализаторам? Это может повлиять на то, что я написал ниже.

Плагин pytest-django использует метку django_db , которая обрабатывается в _django_db_marker в plugin.py (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 и сделать этот каноническим билетом для этой проблемы.

👍 спасибо!

Я также был бы очень признателен за эту функцию.

@мкокотович
Сколько? Достаточно, чтобы сделать это самостоятельно? ;)

В любом случае, основная/фундаментальная проблема здесь (из исходного комментария) уже заключается в том, что БД сбрасывается во время тестов, поэтому нет тривиального способа иметь такое приспособление с областью действия сеанса.

Что может сработать, так это что-то вместе:

@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

извините, я хочу уточнить, так как я считаю, что столкнулся с той же проблемой:

Если я хочу создать фикстуру, основанную на создании нового объекта db, я подумал, что могу сделать

@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 рейтинги