Django单元测试需要很长时间来创建测试数据库

l5tcr1uw  于 2023-04-13  发布在  Go
关注(0)|答案(6)|浏览(233)

一段时间以来,我的单元测试花费了比预期更长的时间。我试着调试了几次,但没有太大的成功,因为延迟甚至在我的测试开始运行之前。这影响了我远程做任何接近测试驱动开发的事情的能力(也许我的期望太高了),所以我想看看我是否可以一劳永逸地解决这个问题。
当运行一个测试时,在开始和实际开始测试之间有70到80秒的延迟。

<... 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

1m:21中的约1m18位于

Creating test database for alias 'default'...

以及

.......

也就是说,测试用了不到3秒的时间,但是数据库初始化好像用了1分18秒
我有大约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.这是正常的,也是意料之中的
1.不期望(希望有修复或引导该做什么)
同样,我不需要关于如何加速测试本身的帮助,但需要初始化(或开销)。我希望上面的例子需要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

我把所有的迁移都压扁了,但还是很慢。

rkue9o1l

rkue9o1l1#

解决我的问题的最终解决方案是强制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 None

    MIGRATION_MODULES = DisableMigrations()

或使用https://pypi.python.org/pypi/django-test-without-migrations
我的整个测试现在需要大约1分钟,一个小应用程序需要5秒。
在我的例子中,测试不需要迁移,因为我在迁移时更新测试,也不使用迁移来添加数据。

sz81bmfz

sz81bmfz2#

摘要

使用pytest

操作

  1. pip install pytest-django
  2. pytest --nomigrations而不是./manage.py test

结果

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

提示

  • 你可以在你的项目根目录下创建一个名为pytest.ini的文件,并在那里指定默认的命令行选项和/或Django设置。
# content of pytest.ini
[pytest]
addopts = --nomigrations
DJANGO_SETTINGS_MODULE = yourproject.settings

现在,您可以简单地使用pytest运行测试,并保存一些输入。

  • 您可以通过将--reuse-db添加到默认命令行选项来进一步加快后续测试的速度。
[pytest]
addopts = --nomigrations --reuse-db

但是,一旦更改了数据库模型,就必须运行一次pytest --create-db来强制重新创建测试数据库。

  • 如果你需要在测试过程中启用gevent monkey patching,你可以在你的项目根目录下创建一个名为pytest的文件,包含以下内容,将执行位强制转换到它(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())

您可以创建一个test_gevent.py文件来测试gevent monkey补丁是否成功:

# -*- coding: utf-8 -*-
# content of test_gevent.py
import time
from django.test import TestCase
from django.db import connection
import gevent

def f(n):
    cur = connection.cursor()
    cur.execute("SELECT SLEEP(%s)", (n,))
    cur.execute("SELECT %s", (n,))
    cur.fetchall()
    connection.close()

class GeventTestCase(TestCase):
    longMessage = True

    def test_gevent_spawn(self):
        timer = time.time()
        d1, d2, d3 = 1, 2, 3
        t1 = gevent.spawn(f, d1)
        t2 = gevent.spawn(f, d2)
        t3 = gevent.spawn(f, d3)
        gevent.joinall([t1, t2, t3])
        cost = time.time() - timer
        self.assertAlmostEqual(cost, max(d1, d2, d3), delta=1.0,
                               msg='gevent spawn not working as expected')

参考资料

mepcadol

mepcadol3#

当manage.py迁移文件中没有更改时,使用./ www.example.com test --keepdb

nzkunb0c

nzkunb0c4#

数据库初始化确实需要太长时间...
我有一个项目,大约有相同数量的模型/表(大约77),大约350个测试,总共需要1分钟来运行所有内容。在一个分配了2个CPU和2GB内存的流浪机器上开发。我还使用py.test和pytest-xdist插件并行运行多个测试。
你可以做的另一件事是告诉django重用测试数据库,只有当你有模式更改时才重新创建它。你也可以使用SQLite,这样测试将使用内存中的数据库。这两种方法都在这里解释:https://docs.djangoproject.com/en/dev/topics/testing/overview/#the-test-database

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

然后你可以使用像这样的库来模拟django对数据库的调用(这是我写的):https://github.com/stphivos/django-mock-queries
通过这种方式,您可以在本地快速运行单元测试,让CI服务器在将代码合并到某个稳定的dev/master分支(不是生产分支)之前,担心运行需要数据库的集成测试。

gywdnpxw

gywdnpxw5#

我也遇到了问题,我所做的一个解决方案是子类化Django.TestCase --〉创建Django.TestCase的子类
并覆盖了如下方法:
@classmethod def _databases_support_transactions(cls):return真
后端数据库是apache cassandra..

a6b3iqyw

a6b3iqyw6#

如果你使用的是Postgres,使用一个Postgres模板来保存一个空数据库的拷贝,然后配置Django在测试数据库创建时使用这个模板。
一种方法是:
1.使用--keepdb参数运行Django的test命令。
1.在Postgres上,使用查询重命名创建的测试数据库
alter database "test_your_db_name" rename to "test_your_db_name_template";
1.在Postgres上,使用查询将数据库设置为模板
alter database "test_your_db_name_template" IS_TEMPLATE = true
1.使用以下方法调整测试设置:

DATABASES["default"]["TEST"] = {}
DATABASES["default"]["TEST"]["TEMPLATE"] = "test_your_db_name_template"

1.在不使用--keepdb参数的情况下再次运行Django测试。由于迁移已经应用到模板数据库,因此它们不必再次运行,因此测试将立即开始。
另请参阅文档:

  • Django TEMPLATE数据库设置

相关问题