Django Unit Testing需要很长时间才能创建测试数据库

时间:2016-04-07 21:58:14

标签: python django django-unittest django-nose

一段时间以来,我的单元测试时间比预期的要长。我试图调试它几次没有太大的成功,因为延迟是在我的测试开始运行之前。这影响了我做任何远程接近测试驱动开发的能力(也许我的期望太高),所以我想知道我是否可以一劳永逸地解决这个问题。

运行测试时,测试开始和实际开始之间有70到80秒的延迟。例如,如果我为一个小模块运行测试(使用time python manage.py test myapp),我会得到

<... bunch of unimportant print messages I print from my settings>

Creating test database for alias 'default'...
......
----------------------------------------------------------------
Ran 6 tests in 2.161s

OK
Destroying test database for alias 'default'...

real    1m21.612s
user    1m17.170s
sys     0m1.400s

大约1m18的1m:21位于

之间
Creating test database for alias 'default'...

.......

线。换句话说,测试需要不到3秒,但数据库初始化似乎需要1:18min

我有大约30个应用程序,大多数有1到3个数据库模型,因此这应该可以了解项目大小。我使用SQLite进行单元测试,并实现了一些建议的改进。我无法发布我的整个设置文件,但很高兴添加所需的任何信息。

我确实使用跑步者

from django.test.runner import DiscoverRunner
from django.conf import settings

class ExcludeAppsTestSuiteRunner(DiscoverRunner):
    """Override the default django 'test' command, exclude from testing
    apps which we know will fail."""

    def run_tests(self, test_labels, extra_tests=None, **kwargs):
        if not test_labels:
            # No appnames specified on the command line, so we run all
            # tests, but remove those which we know are troublesome.
            test_labels = (
                'app1',
                'app2',
                ....
                )
            print ('Testing: ' + str(test_labels))

        return super(ExcludeAppsTestSuiteRunner, self).run_tests(
                test_labels, extra_tests, **kwargs)

并在我的设置中:

TEST_RUNNER = 'config.test_runner.ExcludeAppsTestSuiteRunner'

我也尝试将django-nosedjango-nose-exclude

一起使用

我已经阅读了很多关于如何自己加速测试的内容,但是没有找到关于如何优化或避免数据库初始化的任何线索。我已经看到了关于尝试不使用数据库进行测试的建议,但我不知道或者不知道如何完全避免这种情况。

如果

请告诉我
  1. 这是正常的和预期的
  2. 不期望(并希望能够做什么的修复或领导)
  3. 同样,我不需要如何加速测试本身,但初始化(或开销)。我希望上面的例子花费10秒而不是80秒。

    非常感谢

    我使用--verbose 3运行测试(针对单个应用)并发现这与迁移有关:

      Rendering model states... DONE (40.500s)
      Applying authentication.0001_initial... OK (0.005s)
      Applying account.0001_initial... OK (0.022s)
      Applying account.0002_email_max_length... OK (0.016s)
      Applying contenttypes.0001_initial... OK (0.024s)
      Applying contenttypes.0002_remove_content_type_name... OK (0.048s)
      Applying s3video.0001_initial... OK (0.021s)
      Applying s3picture.0001_initial... OK (0.052s)
      ... Many more like this
    

    我压扁了所有的迁移但仍然很慢。

4 个答案:

答案 0 :(得分:27)

解决我的问题的最终解决方案是强制Django在测试期间禁用迁移,这可以通过这样的设置来完成

TESTING = 'test' in sys.argv[1:]
if TESTING:
    print('=========================')
    print('In TEST Mode - Disableling Migrations')
    print('=========================')

    class DisableMigrations(object):

        def __contains__(self, item):
            return True

        def __getitem__(self, item):
            return "notmigrations"

    MIGRATION_MODULES = DisableMigrations()

或使用https://pypi.python.org/pypi/django-test-without-migrations

我的整个测试现在需要大约1分钟,一个小应用程序需要5秒钟。

在我的情况下,我不需要迁移进行测试,因为我在迁移时更新了测试,并且不使用迁移来添加数据。这不适合所有人

答案 1 :(得分:20)

摘要

使用pytest

操作

  1. pip install pytest-django
  2. pytest --nomigrations代替./manage.py test
  3. 结果

    • ./manage.py test花费2分钟11.86秒
    • pytest --nomigrations费用为2.18秒

    提示

    • 您可以在项目根目录中创建名为pytest.ini的文件,并在那里指定default command line options和/或Django settings

      # content of pytest.ini
      [pytest]
      addopts = --nomigrations
      DJANGO_SETTINGS_MODULE = yourproject.settings
      

      现在您只需使用pytest运行测试,并为您节省一些打字。

    • 通过将--reuse-db添加到默认命令行选项,您可以进一步加快后续测试。

      [pytest]
      addopts = --nomigrations --reuse-db
      

      但是,只要更改了数据库模型,就必须运行pytest --create-db一次到force re-creation of the test database

    • 如果您需要在测试期间启用gevent monkey patching,可以在项目根目录中创建一个名为pytest的文件,其中包含以下内容,将执行位转换为它({{1并运行chmod +x pytest进行测试,而不是./pytest

      pytest

      您可以创建一个#!/usr/bin/env python # -*- coding: utf-8 -*- # content of pytest from gevent import monkey monkey.patch_all() import os os.environ.setdefault("DJANGO_SETTINGS_MODULE", "yourproject.settings") from django.db import connection connection.allow_thread_sharing = True import re import sys from pytest import main if __name__ == '__main__': sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0]) sys.exit(main()) 文件来测试gevent monkey修补是否成功:

      test_gevent.py

    <强>参考

答案 2 :(得分:12)

在迁移文件中没有更改时使用./manage.py test --keepdb

答案 3 :(得分:3)

数据库初始化确实需要太长时间......

我有一个项目,大约有相同数量的模型/表(大约77个),大约350个测试,总共花费1分钟来运行所有内容。在分配了2个cpus和2GB内存的流浪汉机器中进行游戏。我还使用py.test和pytest-xdist插件来并行运行多个测试。

您可以做的另一件事是告诉django重用测试数据库,并且只在更改架构时重新创建它。您也可以使用SQLite,以便测试将使用内存数据库。这两种方法都解释如下 https://docs.djangoproject.com/en/dev/topics/testing/overview/#the-test-database

编辑:如果上述选项都不起作用,还有一个选项是让您的单元测试继承自django SimpleTestCase,或者使用不创建数据库的自定义测试运行器在这里的答案中解释:django unit tests without a db

然后你可以使用像这样的库来模拟对数据库的django调用(我承认这是我写的):https://github.com/stphivos/django-mock-queries

通过这种方式,您可以快速在本地运行单元测试,并让CI服务器担心运行需要数据库的集成测试,然后再将代码合并到一个不是生产单元的稳定dev / master分支。 / p>