在Django 1.7中运行单元测试时禁用迁移


110

Django 1.7引入了数据库迁移

在Django 1.7中运行单元测试时,它会强制进行迁移,这需要很长时间。因此,我想跳过django迁移,并以最终状态创建数据库。

我知道忽略迁移可能是一个坏习惯,因为该部分代码将不会进行测试。事实并非如此:我正在CI测试服务器(jenkins)中运行完整迁移。我只想在速度很重要的本地测试中跳过迁移。


一些背景:

直到Django 1.6为止,在使用South时,我使用SOUTH_TESTS_MIGRATE设置:

默认情况下,South的syncdb命令如果以非交互模式运行(包括运行测试的时间),也会应用迁移-它将在每次运行测试时运行每次迁移。

如果您希望测试运行程序使用syncdb而不是进行迁移(例如,如果迁移时间太长而无法应用),则只需在settings.py中设置SOUTH_TESTS_MIGRATE = False。

但是,syncdb不再存在,现在可以进行迁移

从Django 1.8开始,我将使用--keepdb参数:

--keepdb选项可用于在两次测试运行之间保留测试数据库。这具有跳过创建和销毁操作的优势,这大大减少了运行测试的时间,尤其是大型测试套件中的测试。如果测试数据库不存在,它将在第一次运行时创建,然后为以后的每次运行保留。在运行测试套件之前,所有未应用的迁移也将应用于测试数据库。

因此,此问题仅限于Django 1.7。


我认为,在UT期间,您实际上并没有以测试迁移的方式来运行迁移,因为您开始使用的迁移数据库不存在。测试迁移实际上仅在迁移现有数据库时才发生。这个1.7迁移业务是我与Django一起经历的第一个真正的毛刺,但这是一个很大的刺激因素。南方至少为迁移选择了正确的测试方案。
boatcoder 2014年

django-test-without-migrations软件包对我来说真的很方便,您可能想将接受的答案更改为stackoverflow.com/a/28993456/200224
Andy

如果可能,我宁愿避免添加新的依赖项。
David Arcos

Answers:


79

看一下由Bernie Sumption发布到Django开发人员邮件列表的解决方法

如果尚未运行makemigrations,则“ migrate”命令将应用程序视为未迁移,并直接从模型中创建表,就像syncdb在1.6中所做的那样。我为单元测试定义了一个新的设置模块,名为“ settings_test.py”,该模块从主设置模块导入*并添加以下行:

MIGRATION_MODULES = {“ myapp”:“ myapp.migrations_not_used_in_tests”}

然后我运行如下测试:

DJANGO_SETTINGS_MODULE =“ myapp.settings_test” python manage.py测试

傻瓜会迁移到认为该应用程序未迁移的状态,因此每次创建测试数据库时,它都会反映出models.py的当前结构。

在Django 1.9中,这种情况有所改善,您可以将值设置为None

MIGRATION_MODULES = {“ myapp”:无}


9
请注意该myapp.migrations_not_used_in_tests模块不应该存在。
bmihelac

4
除了关于模块不存在的@bmihelac注释之外,模块字符串还必须包含子字符串“ migrations”,有关原因,请参见:github.com/django/django/blob/stable/1.7.x/django/db/migrations /…
nealtodd 2014年

7
在settings_test.py中动态构建MIGRATION_MODULES的功能要点:gist.github.com/nealtodd/2869341f38f5b1eeb86d
nealtodd 2014年

1
TY。因此,我能够将单元测试从13秒减少到4秒。此外,通过使用sqlite进行测试,可以发现更多的速度提升。对我来说,使用postgres进行测试需要5.5秒,而sqlite需要4秒。
Gattster 2014年

21
从@nealtodd的要点评论中,这里有一个解决方案的链接,该解决方案避免了一些陷阱,而且非常简单:gist.github.com/NotSqrt/5f3c76cd15e40ef62d09
djsutho 2014年

72

这是我的设置文件的结尾:

class DisableMigrations(object):

    def __contains__(self, item):
        return True

    def __getitem__(self, item):
        return None


TESTS_IN_PROGRESS = False
if 'test' in sys.argv[1:] or 'jenkins' in sys.argv[1:]:
    logging.disable(logging.CRITICAL)
    PASSWORD_HASHERS = (
        'django.contrib.auth.hashers.MD5PasswordHasher',
    )
    DEBUG = False
    TEMPLATE_DEBUG = False
    TESTS_IN_PROGRESS = True
    MIGRATION_MODULES = DisableMigrations()

根据此片段

我仅在测试运行时才禁用迁移


1
真好!我还要添加__setitem__(self, *_)方法,因为我们在设置自己的迁移的应用程序时遇到了问题,例如 settings.MIGRATION_MODULES['chroniker'] = 'db_migrations'
Zhe Li

1
非常感谢您,这是我发现唯一有效的方法。
fluffels

在并行模式下运行测试时,这在Django 1.9中不再起作用。使用正常的非并行测试,它可以继续正常运行,但是切换到并行模式会导致找不到表的错误。
LS55321 '16

@LeeSemel在并行模式下,您可能想使用rlmv的解决方案
Guillaume Vincent

@guillaumevincent我在并行模式下使用django-test-without-migrations时遇到相同的问题
LS55321 '16


3

更新:没关系,此更改在1.10最终版发布之前已恢复。希望它将在将来的版本中返回。


请注意,从Django 1.10开始,这可以通过测试数据库设置来控制。

迁移

默认: True

如果设置为False,则Django将不会使用迁移来创建测试数据库。



1

对于django 1.9及更高版本,Guillaume Vincent的答案不再起作用,因此,这里有一个新的解决方案:

我在设置文件中使用了以下代码段, INSTALLED_APPS

if os.environ.get('TESTS_WITHOUT_MIGRATIONS', False):
    MIGRATION_MODULES = {
        app.split('.')[-1]: None for app in INSTALLED_APPS
    }

它遍历所有已安装的应用程序,并将每个应用程序标记为没有迁移模块。有关更多信息,请参见django文档

使用此代码段,您可以运行测试,并设置环境变量TESTS_WITHOUT_MIGRATIONS,例如:

TESTS_WITHOUT_MIGRATIONS=1 ./manage.py test

1

我只是想知道如何在django 1.10之后禁用迁移,可能对某些人有帮助。这是git的链接

class DisableMigrations(dict):
    def __contains__(self, item):
        return True

    def __getitem__(self, item):
        return None

DATABASES = DisableMigrations()

MIGRATION_MODULES = DisableMigrations()

django 1.10的迁移分为两个部分,请看一下load_diskrecorder

的一部分load_disk用于应用程序的迁移模式,在加入INSTALL_APP 和部分recorder1.9之前的数据库连接对于版本,我们需要设置MIGRATION_MODULES={'do.not.migrate':'notmigrations'},当你正在运行测试现在,我们需要把它都不喜欢MIGRATION_MODULES={'do.not.migrate':None} 所以,如果我们不想对任何应用程序使迁移,只是扩展字典和返回Nonegetitem功能,做同样的DATABASES,那就是你需要做的正确的事

PS:对于命令,您需要--setting=module.path.settings_test_snippettest PPS之后指定。如果您正在使用pycharm请不要在中设置--settings 选项Run/Debug configurations,而只需settings_test_snippet.py在自定义设置中添加的路径即可。很好!

请享用

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.