Django的unit testing没有数据库
有没有可能写django unittests而不build立一个数据库? 我想testing不需要db设置的业务逻辑。 虽然build立一个数据库很快,但在某些情况下我并不需要它。
您可以inheritanceDjangoTestSuiteRunner并覆盖setup_databases和teardown_databases方法来传递。
创build一个新的设置文件,并将TEST_RUNNER设置为刚刚创build的新类。 然后,当您运行testing时,使用–settings标志指定新的设置文件。
这是我做的:
创build一个类似于以下的自定义testing服:
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
创build一个自定义设置:
from mysite.settings import * # Test runner with no database creation TEST_RUNNER = 'mysite.scripts.testrunner.NoDbTestRunner'
当你运行你的testing时,像下面一样运行它–settings标志设置为新的设置文件:
python manage.py test myapp --settings='no_db_settings'
通常,应用程序中的testing可以分为两类
- unit testing,这些testing在日晒的代码片段,并不需要去数据库
- 集成testing用例实际上进入数据库并testing完全集成的逻辑。
Django支持unit testing和集成testing。
unit testing,不需要设置和拆除数据库,这些我们应该从SimpleTestCaseinheritance。
from django.test import SimpleTestCase class ExampleUnitTest(SimpleTestCase): def test_something_works(self): self.assertTrue(True)
对于从TestCaseinheritance而来的集成testing用例inheritance自TransactionTestCase,它将在运行每个testing之前设置并拆除数据库。
from django.test import TestCase class ExampleIntegrationTest(TestCase): def test_something_works(self): #do something with database self.assertTrue(True)
这个策略将确保仅在访问数据库的testing用例中创build和销毁数据库,因此testing将更有效率
从django.test.simple
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
我select从django.test.runner.DiscoverRunner
inheritance,并对run_tests
方法进行一些添加。
我的第一个加法检查是否需要设置一个数据库,并允许正常的setup_databases
function踢,如果一个数据库是必要的。 如果允许setup_databases
方法运行,我的第二个添加允许正常的teardown_databases
运行。
我的代码假设从django.test.TransactionTestCase
(因此django.test.TestCase
)inheritance的任何TestCase需要安装一个数据库。 我做了这个假设,因为Django文档说:
如果您需要其他更复杂和重量级的Django特定function,例如…testing或使用ORM …则应使用TransactionTestCase或TestCase。
https://docs.djangoproject.com/en/1.6/topics/testing/tools/#django.test.SimpleTestCase
mysite的/脚本/ settings.py
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)
最后,我将下面一行添加到我的项目的settings.py文件中。
mysite的/ settings.py
TEST_RUNNER = 'mysite.scripts.settings.MyDiscoverRunner'
现在,只运行非db相关的testing时,我的testing套件运行速度要快一个数量级! 🙂
更新:也看到使用第三方工具pytest
这个答案 。
@塞萨尔是对的。 意外运行./manage.py test --settings=no_db_settings
,没有指定应用程序名称,我的开发数据库就被清除了。
为了更安全的方式,使用相同的NoDbTestRunner
,但要配合下面的mysite/no_db_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'
您需要使用外部数据库工具创build名为_test_mysite_db
的数据库。 然后运行以下命令来创build相应的表格:
./manage.py syncdb --settings=mysite.no_db_settings
如果您使用的是南,也运行以下命令:
./manage.py migrate --settings=mysite.no_db_settings
好!
您现在可以通过以下方式快速(安全地)运行unit testing:
./manage.py test myapp --settings=mysite.no_db_settings
作为修改设置以使NoDbTestRunner“安全”的替代方法,下面是NoDbTestRunner的修改版本,它closures当前的数据库连接,并从设置和连接对象中删除连接信息。 为我工作,在依靠它之前在你的环境中testing它:)
class NoDbTestRunner(DjangoTestSuiteRunner): """ A test runner to test without database creation """ def __init__(self, *args, **kwargs): # hide/disconnect databases to prevent tests that # *do* require a database which accidentally get # run from altering your data from django.db import connections from django.conf import settings connections.databases = settings.DATABASES = {} connections._connections['default'].close() del connections._connections['default'] super(NoDbTestRunner,self).__init__(*args,**kwargs) 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
上述解决scheme也很好。 但是如果有更多的迁移次数,下面的解决scheme也会减less数据库的创build时间。 在unit testing期间,运行syncdb而不是运行所有的南迁移将会快得多。
SOUTH_TESTS_MIGRATE = False#取消迁移并使用syncdb
我的networking主机只允许从他们的Web GUI创build和删除数据库,所以当试图运行python manage.py test
时,出现“创buildtesting数据库时出现错误:Permission denied”错误。
我希望将–keepdb选项用于django-admin.py,但从Django 1.7开始似乎不再支持。
我最终做的是修改… / django / db / backends / creation.py中的Django代码,特别是_create_test_db和_destroy_test_db函数。
对于_create_test_db
我注释掉了cursor.execute("CREATE DATABASE ...
行,并将其replace为pass
所以try
块不会为空。
对于_destroy_test_db
我刚刚注释掉了cursor.execute("DROP DATABASE
– 我不需要用任何东西replace它,因为块中已经有另一个命令了( time.sleep(1)
)。
之后,我的testing运行良好 – 虽然我做了一个test_版本的常规数据库分开。
当然,这不是一个好的解决scheme,因为如果Django升级,它会中断,但是由于使用了virtualenv,所以我有了一个Django的本地副本,所以至less我可以控制何时升级到更新的版本。