代码之家  ›  专栏  ›  技术社区  ›  ablerman

如何为特定的测试使用定制的testrunner?[副本]

  •  1
  • ablerman  · 技术社区  · 14 年前

    有没有可能在不设置数据库的情况下编写django单元测试?我想测试不需要db设置的业务逻辑。虽然它是快速建立一个数据库,我真的不需要它在某些情况下。

    0 回复  |  直到 14 年前
        1
  •  124
  •   Mauro Baraldi    7 年前

    您可以将DjangoTestSuiteRunner子类化,并重写setup\u databases和teardown\u databases方法以传递。

    创建一个新的设置文件,并将TEST\u RUNNER设置为刚刚创建的新类。然后在运行测试时,使用--settings标志指定新的设置文件。

    创建一个类似于以下内容的自定义测试套件运行程序:

    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月

    自Django1.8以来,该模块 django.test.simple.DjangoTestSuiteRunner were moved 'django.test.runner.DiscoverRunner' .

    更多信息请查看 official doc 关于自定义测试运行程序的部分。

        2
  •  82
  •   Flimm D. Ben Knoble    5 年前

    一般来说,应用程序中的测试可以分为两类

    1. 单元测试,这些测试在隔离中测试单个代码片段,不需要转到数据库
    2. 集成测试用例,它们实际进入数据库并测试完全集成的逻辑。

    单元测试,不需要设置和拆卸数据库,这些我们应该继承 SimpleTestCase .

    from django.test import SimpleTestCase
    
    
    class ExampleUnitTest(SimpleTestCase):
        def test_something_works(self):
            self.assertTrue(True)
    

    对于集成,测试用例从TestCase继承,反过来又从TransactionTestCase继承,它将在运行每个测试之前设置并拆除数据库。

    from django.test import TestCase
    
    
    class ExampleIntegrationTest(TestCase):
        def test_something_works(self):
            #do something with database
            self.assertTrue(True)
    

    此策略将确保数据库的创建和销毁只针对访问数据库的测试用例,因此测试将更加高效

        3
  •  29
  •   self.    10 年前

    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
    
        4
  •  9
  •   Paul    10 年前

    我选择继承 django.test.runner.DiscoverRunner 并在 run_tests

    我的第一个附加检查,看看是否设置一个数据库是必要的,并允许正常的 setup_databases 如果需要db,则启动的功能。我的第二次添加允许正常的 teardown_databases 如果 设置\u数据库 方法被允许运行。

    我的代码假设从 django.test.TransactionTestCase (因此 django.test.TestCase )需要设置数据库。我做这个假设是因为Django医生说:

    https://docs.djangoproject.com/en/1.6/topics/testing/tools/#django.test.SimpleTestCase

    mysite/脚本/设置.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)
    

    最后,我在项目的设置.py文件。

    TEST_RUNNER = 'mysite.scripts.settings.MyDiscoverRunner'
    

    现在,当只运行非db依赖的测试时,我的测试套件运行速度快了一个数量级!:)

        5
  •  6
  •   Community CDub    8 年前

    更新时间: 另请参见 this answer 使用第三方工具 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'
    

    您需要创建一个名为 _test_mysite_db

    ./manage.py syncdb --settings=mysite.no_db_settings
    

    如果使用的是South,请同时运行以下命令:

    ./manage.py migrate --settings=mysite.no_db_settings
    

    现在,您可以通过以下方式快速(安全)运行单元测试:

    ./manage.py test myapp --settings=mysite.no_db_settings
    
        6
  •  2
  •   Tecuya    11 年前

    作为修改设置以使NoDbTestRunner“安全”的替代方法,下面是NoDbTestRunner的修改版本,它关闭当前数据库连接并从设置和连接对象中删除连接信息。对我有用,在依赖它之前在您的环境中测试它:)

    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
    
        7
  •  2
  •   Kurt Peek    7 年前

    另一个解决方案是让您的测试类从 unittest.TestCase 而不是任何Django的测试类。Django医生( https://docs.djangoproject.com/en/2.0/topics/testing/overview/#writing-tests )包含以下警告:

    使用unittest.TestCase测试用例避免了在事务中运行每个测试和刷新数据库的成本,但是如果您的测试与数据库交互,那么它们的行为将根据测试运行程序执行它们的顺序而有所不同。这可能导致单元测试在隔离运行时通过,但在套件中运行时失败。

    但是,如果您的测试不使用数据库,则不必担心此警告,您可以获得不必在事务中运行每个测试用例的好处。

        8
  •  0
  •   venkat    11 年前

    上述解决方案也很好。但是,如果迁移次数更多,下面的解决方案也会减少数据库创建时间。 在单元测试期间,运行syncdb而不是运行所有的south迁移将快得多。

    SOUTH_TESTS_MIGRATE=False#禁用迁移并使用syncdb

        9
  •  0
  •   Chirael    10 年前

    我的web主机只允许从它们的web GUI创建和删除数据库,因此在尝试运行时出现“Get a error creating the test database:Permission denied”错误 python manage.py test .

    我最后做的是修改…/Django/db/backends中的Django代码/创建.py,具体来说是创建测试数据库和销毁测试数据库函数。

    为了 _create_test_db 我把那本书注释掉了 cursor.execute("CREATE DATABASE ... 并将其替换为 pass 所以 try 街区不会是空的。

    为了 _destroy_test_db 我刚刚评论了一下 cursor.execute("DROP DATABASE time.sleep(1) ).

    在那之后,我的测试运行得很好——尽管我确实单独设置了一个常规数据库的测试版本。

    当然,这不是一个很好的解决方案,因为如果升级Django,它会崩溃,但是由于使用virtualenv,我有一个Django的本地副本,所以至少我可以控制何时/是否升级到新版本。

        10
  •  0
  •   Simone    7 年前

    SimpleTestCase仍然尝试连接到我的测试数据库并运行迁移。当我进行配置/设置时/测试.py文件,然后我的单元测试运行没有它。它允许我使用具有外键和唯一约束字段的模型。(需要db查找的反向外键查找失败。)

    PS代码段

    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
    
        11
  •  0
  •   radtek    6 年前

    使用鼻子测试跑步器(django nose)时,可以执行以下操作:

    my_project/lib/nodb_test_runner.py :

    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
    

    在你的 settings.py

    TEST_RUNNER = 'lib.nodb_test_runner.NoDbTestRunner' . # Was 'django_nose.NoseTestSuiteRunner'

    或者

    我只想让它运行特定的测试,所以我像这样运行它:

    python manage.py test integration_tests/integration_*  --noinput --testrunner=lib.nodb_test_runner.NoDbTestRunner
    
        12
  •  0
  •   Frank    4 年前

    您可以将数据库设置为普通TestCase中的空列表django.测试.

    from django.test import TestCase
    
    class NoDbTestCase(TestCase):
        databases = []