Очистка после модульного теста, который утверждает IntegrityError

У меня есть модель Django с "названием" CharField(unique=True). У меня есть модульный тест, который утверждает, что создание второго экземпляра с тем же заголовком вызывает ошибку IntegrityError. (Я использую pytest и pytest-django.)

У меня есть что-то вроде:

class Foo(models.Model):
    title = models.CharField(unique=True)

def test_title_is_unique(db):
    Foo.objects.create(title='foo')
    with pytest.raises(IntegrityError):
        Foo.objects.create(title='foo')

Это работает нормально, за исключением того, что приведенный выше код не включает код очистки. pytest-django не очищает базу данных за вас, поэтому вам необходимо зарегистрировать обработчики очистки при создании или сохранении экземпляра модели. Что-то вроде этого:

def test_title_is_unique(request, db):
    foo = Foo.objects.create(title='foo')
    request.addfinalizer(foo.delete)
    with pytest.raises(IntegrityError):
        Foo.objects.create(title='foo')

Хорошо, это нормально. Но что, если второй вызов .create() окажется ошибочным? Я все еще хочу очистить этот экземпляр, но только если он (ошибочно) будет создан.

Вот на чем я остановился:

def test_title_is_unique(request, db):
    foo = Foo.objects.create(title='foo')
    request.addfinalizer(foo.delete)
    try:
        with pytest.raises(IntegrityError):
            new_foo = Foo.objects.create(title='foo')
    finally:
        if 'new_foo' in locals():
            request.addfinalizer(new_foo.delete)

Это не кажется особенно элегантным или Pythonic, не говоря уже о том, что есть куча строк кода, которые действительно не должны запускаться.

Как я могу гарантировать, что второй экземпляр модели будет очищен, если он создан, но с меньшим количеством обручей для перехода и/или с использованием меньшего количества строк кода?


person Frank T    schedule 19.03.2014    source источник


Ответы (1)


Вам не нужно беспокоиться об очистке. Прибор pytest-django db отключает транзакции для теста, выполняя весь тест в одной транзакции и откатывая транзакцию в конце. Это гарантирует, что база данных останется чистой.

Если для теста требуется транзакция, есть приспособление transactional_db, которое активирует транзакции (медленнее) и сбрасывает все содержимое базы данных после теста (очень медленно), снова очищая вас.

Поэтому, если очистка не происходит, вам, вероятно, следует сообщить об ошибке в pytest-django. Но я был бы удивлен, если бы это было так, если бы я не пропустил что-то важное.

person flub    schedule 19.03.2014
comment
Я не вижу, чтобы pytest вообще очищал базу данных между тестами. Позвольте мне немного покопаться и посмотреть, не странная ли моя установка, а если нет, я позабочусь об этом с помощью pytest-django. - person Frank T; 19.03.2014
comment
Ты прав. Я не упомянул, что мой проект имеет несколько баз данных. По-видимому, TestCase Django (который pytest-django использует под капотом для приспособления db) имеет поле multi_db, которое по умолчанию имеет значение False. Вот соответствующий вопрос SO: stackoverflow.com/questions/10121485/ - person Frank T; 19.03.2014
comment
Итак, вы вернулись к использованию классов Django UnitTest, как предлагает документация pytest-djagno? Если вы используете эту функцию Django, было бы здорово, если бы вы помогли разработать хороший API в pytest-django для этого, чтобы мы могли правильно реализовать это в pytest-django. Вероятно, вы можете начать обсуждение либо по проблеме pytest-django, либо в основном списке рассылки py.test. - person flub; 20.03.2014
comment
Я открыл задачу на Github: github.com/pelme/pytest_django/issues/76 Я открыт для идей об API для включения его в pytest-django. - person Frank T; 20.03.2014