django unit tests without a db

前端 未结 11 1460
醉梦人生
醉梦人生 2020-11-29 16:11

Is there a possibility to write django unittests without setting up a db? I want to test business logic which doesn\'t require the db to set up. And while it is fast to setu

相关标签:
11条回答
  • 2020-11-29 16:45

    My web host only allows creating and dropping databases from their Web GUI, so I was getting a "Got an error creating the test database: Permission denied" error when trying to run python manage.py test.

    I'd hoped to use the --keepdb option to django-admin.py but it doesn't seem to be supported any longer as of Django 1.7.

    What I ended up doing was modifying the Django code in .../django/db/backends/creation.py, specifically the _create_test_db and _destroy_test_db functions.

    For _create_test_db I commented out the cursor.execute("CREATE DATABASE ... line and replaced it with pass so the try block wouldn't be empty.

    For _destroy_test_db I just commented out cursor.execute("DROP DATABASE - I didn't need to replace it with anything because there was already another command in the block (time.sleep(1)).

    After that my tests ran fine - though I did set up a test_ version of my regular database separately.

    This isn't a great solution of course, because it will break if Django is upgraded, but I had a local copy of Django due to using virtualenv so at least I have control over when/if I upgrade to a newer version.

    0 讨论(0)
  • 2020-11-29 16:47

    The above solutions are fine too. But the following solution will also reduce the db creation time if there are more number of migrations. During unit testing, running syncdb instead of running all the south migrations will be much faster.

    SOUTH_TESTS_MIGRATE = False # To disable migrations and use syncdb instead

    0 讨论(0)
  • 2020-11-29 16:49

    From django.test.simple

      warnings.warn(
          "The django.test.simple module and DjangoTestSuiteRunner are deprecated; "
          "use django.test.runner.DiscoverRunner instead.",
          RemovedInDjango18Warning)
    

    So override DiscoverRunner instead of 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
    

    Use like that :

    python manage.py test app --testrunner=app.filename.NoDbTestRunner
    
    0 讨论(0)
  • 2020-11-29 16:51

    I chose to inherit from django.test.runner.DiscoverRunner and make a couple of additions to the run_tests method.

    My first addition checks to see if setting up a db is necessary and allows the normal setup_databases functionality to kick in if a db is necessary. My second addition allows the normal teardown_databases to run if the setup_databases method was allowed to run.

    My code assumes that any TestCase that inherits from django.test.TransactionTestCase (and thus django.test.TestCase) requires a database to be setup. I made this assumption because the Django docs say:

    If you need any of the other more complex and heavyweight Django-specific features like ... Testing or using the ORM ... then you should use TransactionTestCase or TestCase instead.

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

    mysite/scripts/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)
    

    Finally, I added the following line to my project's settings.py file.

    mysite/settings.py

    TEST_RUNNER = 'mysite.scripts.settings.MyDiscoverRunner'
    

    Now, when running only non-db-dependent tests, my test suite runs an order of magnitude faster! :)

    0 讨论(0)
  • 2020-11-29 16:53

    You can subclass DjangoTestSuiteRunner and override setup_databases and teardown_databases methods to pass.

    Create a new settings file and set TEST_RUNNER to the new class you just created. Then when you're running your test, specify your new settings file with --settings flag.

    Here is what I did:

    Create a custom test suit runner similar to this:

    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
    

    Create a custom settings:

    from mysite.settings import *
    
    # Test runner with no database creation
    TEST_RUNNER = 'mysite.scripts.testrunner.NoDbTestRunner'
    

    When you're running your tests, run it like the following with --settings flag set to your new settings file:

    python manage.py test myapp --settings='no_db_settings'
    

    UPDATE: April/2018

    Since Django 1.8, the module django.test.simple.DjangoTestSuiteRunner were moved to 'django.test.runner.DiscoverRunner'.

    For more info check official doc section about custom test runners.

    0 讨论(0)
  • 2020-11-29 16:53

    Updated: also see this answer for using a third-party tool pytest.


    @Cesar is right. After accidentally running ./manage.py test --settings=no_db_settings, without specifying an app name, my development database was wiped out.

    For a safer manner, use the same NoDbTestRunner, but in conjunction with the following 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'
    

    You need to create a database called _test_mysite_db using an external database tool. Then run the following command to create the corresponding tables:

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

    If you're using South, also run the following command:

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

    OK!

    You can now run unit tests blazingly fast (and safe) by:

    ./manage.py test myapp --settings=mysite.no_db_settings
    
    0 讨论(0)
提交回复
热议问题