没有db的django单元测试
是否有可能在不设置db的情况下编写django单元测试?我想测试不需要设置db的业务逻辑。虽然设置数据库很快,但在某些情况下我确实不需要它。您可以将DjangTestSuiteRunner子类化,并覆盖要传递的设置数据库和拆卸数据库方法 创建一个新的设置文件,并将TEST_RUNNER设置为刚创建的新类。然后在运行测试时,使用--settings标志指定新的设置文件 以下是我所做的: 创建一个类似以下内容的自定义测试套装跑步者:没有db的django单元测试,django,testing,Django,Testing,是否有可能在不设置db的情况下编写django单元测试?我想测试不需要设置db的业务逻辑。虽然设置数据库很快,但在某些情况下我确实不需要它。您可以将DjangTestSuiteRunner子类化,并覆盖要传递的设置数据库和拆卸数据库方法 创建一个新的设置文件,并将TEST_RUNNER设置为刚创建的新类。然后在运行测试时,使用--settings标志指定新的设置文件 以下是我所做的: 创建一个类似以下内容的自定义测试套装跑步者: from django.test.simple import Dj
from django.test.simple import DjangoTestSuiteRunner
class NoDbTestRunner(DjangoTestSuiteRunner):
""" A test runner to test without database creation """
def setup_databases(self, **kwargs):
""" Override the database creation defined in parent class """
pass
def teardown_databases(self, old_config, **kwargs):
""" Override the database teardown defined in parent class """
pass
创建自定义设置:
from mysite.settings import *
# Test runner with no database creation
TEST_RUNNER = 'mysite.scripts.testrunner.NoDbTestRunner'
运行测试时,请按如下方式运行,并将--settings标志设置为新的设置文件:
python manage.py test myapp --settings='no_db_settings'
更新日期:2018年4月
自Django 1.8以来,模块Django.test.simple.DjangoTestSuiteRunner
到'Django.test.runner.DiscoverRunner'
有关更多信息,请查看有关自定义测试运行程序的部分。更新:另请参阅有关使用第三方工具的信息
pytest
@塞萨尔是对的。在意外运行了
/manage.py test--settings=no\u db\u settings
,但没有指定应用程序名称后,我的开发数据库被清除
为了更安全,请使用相同的nodbestrunner
,但与以下mysite/no\u db\u settings.py
结合使用:
from mysite.settings import *
# Test runner with no database creation
TEST_RUNNER = 'mysite.scripts.testrunner.NoDbTestRunner'
# Use an alternative database as a safeguard against accidents
DATABASES['default']['NAME'] = '_test_mysite_db'
from django_nose import NoseTestSuiteRunner
class NoDbTestRunner(NoseTestSuiteRunner):
"""
A test runner to test without database creation/deletion
Used for integration tests
"""
def setup_databases(self, **kwargs):
pass
def teardown_databases(self, old_config, **kwargs):
pass
您需要使用外部数据库工具创建一个名为\u test\u mysite\u db
的数据库。然后运行以下命令创建相应的表:
./manage.py syncdb --settings=mysite.no_db_settings
如果使用的是South,请同时运行以下命令:
./manage.py migrate --settings=mysite.no_db_settings
好的
您现在可以通过以下方式快速(安全)运行单元测试:
作为修改设置以使NodeBtestRunner“安全”的替代方法,下面是NodeBtestRunner的修改版本,它关闭当前数据库连接并从设置和连接对象中删除连接信息。适用于我,在依赖它之前在您的环境中进行测试:)
通常,应用程序中的测试可分为两类
from django.test import TestCase
class ExampleIntegrationTest(TestCase):
def test_something_works(self):
#do something with database
self.assertTrue(True)
此策略将确保仅为访问数据库的测试用例创建和销毁数据库,因此测试将更加高效
warnings.warn(
"The django.test.simple module and DjangoTestSuiteRunner are deprecated; "
"use django.test.runner.DiscoverRunner instead.",
RemovedInDjango18Warning)
因此,重写DiscoverRunner
,而不是DjangoTestSuiteRunner
from django.test.runner import DiscoverRunner
class NoDbTestRunner(DiscoverRunner):
""" A test runner to test without database creation/deletion """
def setup_databases(self, **kwargs):
pass
def teardown_databases(self, old_config, **kwargs):
pass
这样使用:
python manage.py test app --testrunner=app.filename.NoDbTestRunner
上述解决方案也很好。但是,如果迁移次数更多,下面的解决方案也将减少数据库创建时间。 在单元测试期间,运行syncdb而不是运行所有的南方迁移将快得多 SOUTH_TESTS_MIGRATE=False#禁用迁移并使用syncdb 反而
我选择继承自
django.test.runner.DiscoverRunner
,并对run\u tests
方法进行了一些添加
我第一次添加的内容是检查是否需要设置数据库,并允许在需要数据库时启动正常的setup\u databases
功能。如果允许运行setup\u databases
方法,我的第二次添加允许正常的teardown\u databases
运行
我的代码假设从django.test.TransactionTestCase
(因此django.test.TestCase
)继承的任何测试用例都需要设置数据库。我做出这个假设是因为Django文档说:
如果您需要任何其他更复杂和重量级的Django特定功能,如。。。测试或使用ORM。。。然后应该使用TransactionTestCase或TestCase
mysite/scripts/settings.py
最后,我在项目的settings.py文件中添加了以下行
mysite/settings.py
现在,当只运行不依赖于db的测试时,我的测试套件运行速度要快一个数量级 我的web主机只允许从他们的web GUI创建和删除数据库,因此我在尝试运行
python manage.py测试时遇到了“创建测试数据库时出错:权限被拒绝”错误
我本来希望对django-admin.py使用--keepdb选项,但从django 1.7开始,它似乎不再受支持
最后我修改了…/Django/db/backends/creation.py中的Django代码,特别是_create\u test\u db和_destroy\u test\u db函数
对于\u create\u test\u db
我注释掉了光标。execute(“create DATABASE…
行)并将其替换为pass
,这样try
块就不会为空
对于\u destroy\u test\u db
我刚刚注释掉了cursor.execute(“DROP DATABASE
-我不需要用任何东西替换它,因为块中已经有另一个命令(time.sleep(1)
)
在那之后,我的测试运行良好——尽管我确实单独设置了常规数据库的测试版本
这当然不是一个很好的解决方案,因为如果Django升级,它会崩溃,但由于使用virtualenv,我有一个Django的本地副本,所以至少我可以控制何时/是否升级到新版本。另一个解决方案是让您的测试类只从unittest.TestCase
继承,而不是从Django的任何一个测试用例继承Django文档()包含以下警告
from django.test.runner import DiscoverRunner
class NoDbTestRunner(DiscoverRunner):
""" A test runner to test without database creation/deletion """
def setup_databases(self, **kwargs):
pass
def teardown_databases(self, old_config, **kwargs):
pass
python manage.py test app --testrunner=app.filename.NoDbTestRunner
from django.test import TransactionTestCase
from django.test.runner import DiscoverRunner
class MyDiscoverRunner(DiscoverRunner):
def run_tests(self, test_labels, extra_tests=None, **kwargs):
"""
Run the unit tests for all the test labels in the provided list.
Test labels should be dotted Python paths to test modules, test
classes, or test methods.
A list of 'extra' tests may also be provided; these tests
will be added to the test suite.
If any of the tests in the test suite inherit from
``django.test.TransactionTestCase``, databases will be setup.
Otherwise, databases will not be set up.
Returns the number of tests that failed.
"""
self.setup_test_environment()
suite = self.build_suite(test_labels, extra_tests)
# ----------------- First Addition --------------
need_databases = any(isinstance(test_case, TransactionTestCase)
for test_case in suite)
old_config = None
if need_databases:
# --------------- End First Addition ------------
old_config = self.setup_databases()
result = self.run_suite(suite)
# ----------------- Second Addition -------------
if need_databases:
# --------------- End Second Addition -----------
self.teardown_databases(old_config)
self.teardown_test_environment()
return self.suite_result(suite, result)
TEST_RUNNER = 'mysite.scripts.settings.MyDiscoverRunner'
PROJECT_ROOT_DIR/config/settings/test.py:
from .base import *
#other test settings
#DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': 'PROJECT_ROOT_DIR/db.sqlite3',
# }
#}
cli, run from PROJECT_ROOT_DIR:
./manage.py test path.to.app.test --settings config.settings.test
path/to/app/test.py:
from django.test import SimpleTestCase
from .models import *
#^assume models.py imports User and defines Classified and UpgradePrice
class TestCaseWorkingTest(SimpleTestCase):
def test_case_working(self):
self.assertTrue(True)
def test_models_ok(self):
obj = UpgradePrice(title='test',price=1.00)
self.assertEqual(obj.title,'test')
def test_more_complex_model(self):
user = User(username='testuser',email='hi@hey.com')
self.assertEqual(user.username,'testuser')
def test_foreign_key(self):
user = User(username='testuser',email='hi@hey.com')
ad = Classified(user=user,headline='headline',body='body')
self.assertEqual(ad.user.username,'testuser')
#fails with error:
def test_reverse_foreign_key(self):
user = User(username='testuser',email='hi@hey.com')
ad = Classified(user=user,headline='headline',body='body')
print(user.classified_set.first())
self.assertTrue(True) #throws exception and never gets here
from django_nose import NoseTestSuiteRunner
class NoDbTestRunner(NoseTestSuiteRunner):
"""
A test runner to test without database creation/deletion
Used for integration tests
"""
def setup_databases(self, **kwargs):
pass
def teardown_databases(self, old_config, **kwargs):
pass
python manage.py test integration_tests/integration_* --noinput --testrunner=lib.nodb_test_runner.NoDbTestRunner