【问题标题】:How to unittest a django database migration?如何对 django 数据库迁移进行单元测试?
【发布时间】:2016-05-12 04:04:17
【问题描述】:

我们使用 django 迁移 (django v1.7+) 更改了我们的数据库。 数据库中存在的数据不再有效。

基本上我想通过在单元测试中构建迁移前数据库,添加一些数据,应用迁移,然后确认一切顺利来测试迁移。

怎么做的:

  1. 在加载单元测试时阻止新的迁移

    我找到了 some stuff 关于覆盖 settings.MIGRATION_MODULES 但不知道如何使用它。当我检查executor.loader.applied_migrations 时,它仍然列出了所有内容。我可以阻止新迁移的唯一方法是实际删除文件;不是我可以使用的解决方案。

  2. 在 unittest 数据库中创建一条记录(使用旧模型)

    如果我们可以阻止迁移,那么这应该非常简单。 myModel.object.create(...)

  3. 应用迁移

    我想我现在已经找到了test_executor:设置一个指向迁移文件的计划并执行它,我想我可能可以解决这个问题?嗯,对吧?有任何代码:-D

  4. 确认数据库中的旧数据现在与新模型匹配

    同样,我希望这应该很容易:只需获取迁移前创建的实例并确认它已以所有正确的方式更改。

所以真正的挑战是如何防止单元测试应用最新的迁移脚本,然后在我们准备好时应用它?


也许我的方法有误?我应该创建固定装置,并确认它们最后都很好吗?固定装置是在应用迁移之前加载还是在它们全部完成之后加载?


通过使用MigrationExecutor 并使用.migrate 选择特定迁移,我已经能够,也许?,将其回滚到特定状态,然后一个接一个地前滚。但这引发了质疑。由于缺少实际的 ALTER TABLE 指令,目前正在追查 sqlite 捏造的东西。陪审团仍未出局。

【问题讨论】:

  • 版本控制系统可能会在这种情况下有所帮助。您将需要维护两个具有旧迁移和新迁移的分支。它看起来类似于您为具有不同设置、代码等的生产和开发环境有单独的分支的情况。
  • @xyres 检查各种提交通常不属于单元测试的范围。

标签: django django-models django-unittest


【解决方案1】:

我无法阻止单元测试从当前数据库架构开始,但我确实发现恢复到迁移历史中的早期点很容易:

其中“0014_nulls_permitted”是迁移目录中的一个文件...

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

注意: executor.migrate 的调用之间运行executor.loader.build_graph 似乎是完成迁移并使事情按预期运行的一个非常重要的部分

当前适用于数据库的迁移可以通过以下方式检查:

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()

Migration 0015 通过数据库将所有 NULL 字段转换为默认值。迁移 0016 更改了架构。您可以散布一些打印语句,以确认事情是否按照您认为的那样发生。

现在测试可以确认迁移已成功。在这种情况下,通过确保数据库中没有空值。

jobs = Job.objects.all()
self.assertTrue(all([j.next_job_state is not None for j in jobs]))

【讨论】:

  • 我假设 Jobs 之前设置为 apps.get_model('someapp.Job') 这样才能正常工作。正确的?否则,Job 模型可能与模式的状态不兼容。
【解决方案2】:

我们在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 只会发现没有迁移。

【讨论】:

  • 谢谢。这可以确保它找不到任何迁移,但似乎没有多大帮助;如果找不到迁移,我们如何测试它们?
  • 显然我误解了你的问题——你真的想测试迁移逻辑本身吗?
  • 是的...一个创建迁移前数据库的测试,迁移它,然后测试结果。
  • 明白了。 2c:编辑标题以反映这将对 IMO 有所帮助 :)
猜你喜欢
  • 1970-01-01
  • 2011-06-15
  • 1970-01-01
  • 2012-08-11
  • 1970-01-01
  • 2017-10-15
  • 2018-09-12
  • 2021-09-27
  • 1970-01-01
相关资源
最近更新 更多