Как протестировать миграцию базы данных django?

Мы изменили нашу базу данных, используя миграции django (django v1.7+). Данные, существующие в базе данных, больше недействительны.

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

Как один:

  1. сдерживать новую миграцию при загрузке модульного теста

    Я нашел кое-что о переопределении settings.MIGRATION_MODULES, но не смог не понять, как его использовать. Когда я проверяю executor.loader.applied_migrations, он все еще перечисляет все. Единственный способ предотвратить новую миграцию — удалить файл; не решение, которое я могу использовать.

  2. создать запись в базе данных unittest (используя старую модель)

    Если мы сможем предотвратить миграцию, то это должно быть довольно просто. myModel.object.create(...)

  3. применить миграцию

    Я думаю, что теперь я могу решить это, когда нашел test_executor: установить план, указывающий на файл миграции, и выполнить его? Эм, верно? Есть код для этого :-D

  4. подтвердите, что старые данные в базе данных теперь соответствуют новой модели

    Опять же, я ожидаю, что это должно быть довольно просто: просто выберите экземпляр, созданный до миграции, и подтвердите, что он изменился всеми правильными способами.

Таким образом, проблема на самом деле заключается в том, чтобы решить, как предотвратить применение модульным тестом последнего сценария миграции, а затем применить его, когда мы будем готовы?


Может у меня неправильный подход? Должен ли я создавать фикстуры и просто подтверждать, что в конце они все хороши? Фикстуры загружаются до применения миграций или после их завершения?


Используя MigrationExecutor и выбирая определенные миграции с помощью .migrate, я смог, может быть?, откатить его до определенного состояния, а затем выполнить откат вперед один за другим. Но это вызывает сомнения; в настоящее время преследует sqlite, выдумывая из-за отсутствия фактической инструкции ALTER TABLE. Жюри еще нет.


person John Mee    schedule 12.05.2016    source источник
comment
В этой ситуации может помочь система контроля версий. Вам нужно будет поддерживать две ветки со старой и новой миграциями. Это похоже на случай, когда у вас есть отдельные ветки для рабочей среды и среды разработки с разными настройками, кодом и т.д.   -  person xyres    schedule 12.05.2016
comment
@xyres проверка различных коммитов обычно не входит в рамки модульного теста.   -  person John Mee    schedule 12.05.2016


Ответы (2)


Мне не удалось предотвратить запуск модульного теста с текущей схемой базы данных, но я обнаружил, что довольно легко вернуться к более ранним точкам в истории миграции:

Где "0014_nulls_permitted" - это файл в каталоге миграции...

from django.db.migrations.executor import MigrationExecutor
executor.migrate([("workflow_engine", "0014_nulls_permitted")])
executor.loader.build_graph()

Примечание: запуск executor.loader.build_graph между вызовами executor.migrate кажется очень важной частью завершения миграции и обеспечения ожидаемого поведения

Миграции, которые в настоящее время применимы к базе данных, можно проверить с помощью чего-то вроде:

print [x[1] for x in sorted(executor.loader.applied_migrations)]

[u'0001_initial', u'0002_fix_foreignkeys', ... u'0014_nulls_permitted']

Я создал экземпляр модели через ORM, а затем убедился, что база данных находится в старом состоянии, напрямую запустив SQL:

job = Job.objects.create(....)
from django.db import connection
cursor = connection.cursor()
cursor.execute('UPDATE workflow_engine_job SET next_job_state=NULL')

Здорово. Теперь я знаю, что у меня есть база данных в старом состоянии, и я могу протестировать прямую миграцию. Итак, где 0016_nulls_banished — это файл миграции:

executor.migrate([("workflow_engine", "0016_nulls_banished")])
executor.loader.build_graph()

Миграция 0015 проходит через базу данных, преобразуя все поля NULL в значения по умолчанию. Миграция 0016 изменяет схему. Вы можете разбросать несколько печатных утверждений, чтобы подтвердить, что все происходит так, как вы думаете.

И теперь тест может подтвердить, что миграция сработала. В этом случае убедитесь, что в базе данных не осталось нулей.

jobs = Job.objects.all()
self.assertTrue(all([j.next_job_state is not None for j in jobs]))
person John Mee    schedule 13.05.2016
comment
Я предполагаю, что Jobs ранее было установлено на что-то вроде apps.get_model('someapp.Job'), чтобы это работало. Правильный? В противном случае модель Job может быть несовместима с состоянием схемы. - person Steve Jorgensen; 16.10.2019

Мы использовали следующий код в settings_test.py, чтобы игнорировать миграцию для тестов:

MIGRATION_MODULES = dict(
    (app.split('.')[-1], '.'.join([app, 'nonexistent_django_migrations_module']))
    for app in INSTALLED_APPS
)

Идея здесь в том, что ни в одном из приложений нет папки nonexistent_django_migrations_module, и поэтому django просто не найдет миграции.

person zsepi    schedule 12.05.2016
comment
Спасибо. Это работает, чтобы гарантировать, что он не может найти никаких миграций, но, похоже, не очень помогает; как мы можем проверить миграции, если он не может их найти? - person John Mee; 12.05.2016
comment
видимо, я неправильно понял ваш вопрос - вы действительно хотите проверить саму логику миграции? - person zsepi; 12.05.2016
comment
Да... тест, который создает базу данных перед миграцией, переносит ее, а затем проверяет результат. - person John Mee; 12.05.2016
comment
Попался. 2c: редактирование заголовка, чтобы отразить это, было бы полезно IMO :) - person zsepi; 12.05.2016