Python Django单元测试需要很长时间来创建测试数据库
一段时间以来,我的单元测试花费的时间比预期的要长。我试过几次调试它,但都没有成功,因为延迟甚至在我的测试开始运行之前。这影响了我做任何远程接近测试驱动开发的事情的能力(可能我的期望太高了),所以我想看看是否能一劳永逸地解决这个问题 运行测试时,测试的开始和实际开始之间有70到80秒的延迟。例如,如果我运行一个小模块的测试(使用Python Django单元测试需要很长时间来创建测试数据库,python,django,django-unittest,django-nose,Python,Django,Django Unittest,Django Nose,一段时间以来,我的单元测试花费的时间比预期的要长。我试过几次调试它,但都没有成功,因为延迟甚至在我的测试开始运行之前。这影响了我做任何远程接近测试驱动开发的事情的能力(可能我的期望太高了),所以我想看看是否能一劳永逸地解决这个问题 运行测试时,测试的开始和实际开始之间有70到80秒的延迟。例如,如果我运行一个小模块的测试(使用time python manage.py test myapp),我会得到 和 ....... 线路。换句话说,测试在3sec以下进行,但数据库初始化似乎需要1:18分
time python manage.py test myapp
),我会得到
和
.......
线路。换句话说,测试在3sec以下进行,但数据库初始化似乎需要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 nose
与django nose exclude
我已经阅读了很多关于如何加速测试本身的文章,但是没有找到任何关于如何优化或避免数据库初始化的线索。我已经看到了关于尝试不使用数据库进行测试的建议,但我无法或不知道如何完全避免这种情况
如果需要,请告诉我
--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
我压缩了所有迁移,但仍然很慢。数据库初始化确实需要太长时间 我有一个项目,有大约相同数量的模型/表格(大约77个),大约350个测试,总共需要1分钟来运行所有东西。在一台分配了2个CPU和2GB ram的流浪机器中进行设备隔离。我还使用pytest.test和pytest xdist插件并行运行多个测试 您可以做的另一件事是告诉django重用测试数据库,并且只在模式发生更改时重新创建它。您还可以使用SQLite,以便测试将使用内存中的数据库。这里解释了两种方法: 编辑:如果上述选项都不起作用,另一个选项是让您的单元测试从django SimpleTestCase继承,或者使用自定义测试运行程序,该测试运行程序不创建数据库,如下面的回答所述: 然后,您可以使用这样的库(我承认是这样写的)来模拟django对数据库的调用:
这样,您可以在本地快速运行单元测试,让CI服务器在将代码合并到非生产分支的稳定开发/主分支之前,担心运行需要数据库的集成测试。解决我的问题的最终解决方案是强制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()
或使用
我的整个测试现在大约需要1分钟,一个小应用程序需要5秒钟
在我的情况下,测试不需要迁移,因为我在迁移时更新测试,并且不使用迁移添加数据。这对每个人都不起作用总结
使用pytest
操作
pip安装pytest-django
pytest——命名
而不是/manage.py测试
花费2分钟11.86秒/manage.py测试
pytest——命名花费2.18秒
- 您可以在项目根目录中创建名为
的文件,并在其中指定和/或 现在,您可以简单地使用pytest.ini
运行测试,并节省一点键入时间pytest
- 通过添加到默认命令行选项,可以进一步加快后续测试的速度
但是,一旦更改了数据库模型,就必须运行一次[pytest] addopts = --nomigrations --reuse-db
pytest--create db
- 如果需要在测试期间启用,可以在项目根目录中创建名为
的文件,其中包含以下内容,将执行位强制转换为它(pytest
),然后运行chmod+x pytest
进行测试,而不是/pytest
:pytest
您可以创建一个#/usr/bin/env python #-*-编码:utf-8-*- #测试内容 从gevent导入猴子 猴子 导入操作系统 os.environ.setdefault(“DJANGO\u设置\u模块”、“yourproject.SETTINGS”) 从django.db导入连接 connection.allow\u thread\u sharing=True 进口稀土 导入系统 从pytest导入main 如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu': sys.argv[0]=re.sub(r'(-script\.pyw\.exe)?$,'',sys.argv[0]) sys.exit(main())
文件来测试gevent monkey补丁是否成功:test\u gevent.py
#-*-编码:utf-8-*- #测试内容_gevent.py 导入时间 从django.test导入TestCase 从django.db导入连接 导入gevent def f(n): cur=connection.cursor() 当前执行(“选择睡眠(%s)”,(n,) 当前执行(“选择%s”,(n,)) cur.fetchall() 连接。关闭() 类GeventTestCase(测试用例): longMessage=True def测试_gevent_繁殖(自身): 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]) 成本=时间。时间()-计时器 自评资产质量(成本,最大值(d1、d2、d3),增量=1.0, msg='gevent spawn未按预期工作')
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()
# content of pytest.ini
[pytest]
addopts = --nomigrations
DJANGO_SETTINGS_MODULE = yourproject.settings
[pytest]
addopts = --nomigrations --reuse-db