Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Unit Testing taking a very long time to create test database

For some time now, my unit testing has been taking a longer than expected time. I have tried to debug it a couple of times without much success, as the delays are before my tests even begin to run. This has affected my ability to do anything remotely close to test driven development (maybe my expectations are too high), so I want to see if I can fix this once and for all.

When a run a test, there is a 70 to 80sec delay between the start and the actual beginning of the test. For example, if I run a test for a small module (using time python manage.py test myapp), I get

<... bunch of unimportant print messages I print from my settings>  Creating test database for alias 'default'... ...... ---------------------------------------------------------------- Ran 6 tests in 2.161s  OK Destroying test database for alias 'default'...  real    1m21.612s user    1m17.170s sys     0m1.400s 

About 1m18 of the 1m:21 are between the

Creating test database for alias 'default'... 

and the

....... 

line. In other words, the test takes under 3sec, but the database initialization seems to be taking 1:18min

I have about 30 apps, most with 1 to 3 database models so this should give an idea of the project size. I use SQLite for unit testing, and have implemented some of the suggested improvements. I cannot post my whole setting file, but happy to add any information that is required.

I do use a runner

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) 

and in my settings:

TEST_RUNNER = 'config.test_runner.ExcludeAppsTestSuiteRunner' 

I have also tried using django-nose with django-nose-exclude

I have read a lot about how to speed up the test themselves, but have not found any leads on how to optimize or avoid the database initialization. I have seen the suggestions on trying not to test with the database but I cannot or don't know how to avoid that completely.

Please let me know if

  1. This is normal and expected
  2. Not expected (and hopefully a fix or lead on what to do)

Again, I don't need help on how to speed up the test themselves, but the initialization (or overhead). I want the example above to take 10sec instead of 80sec.

Many thanks

I run the test (for single app) with --verbose 3 and discovered this is all related to migrations:

  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 

I squashed all my migrations but still slow.

like image 909
dkarchmer Avatar asked Apr 07 '16 21:04

dkarchmer


People also ask

Why are Django tests so slow?

By default, Django creates a test database for each test run, which is destroyed at the end. This is a rather slow process, especially if you want to run just a few tests! The --keepdb option will not destroy and recreate the database locally on every run.

Should unit tests use real database?

It is meant to make sure that definable modules of code work as expected. To test an application it is not enough to use unit tests. You must also perform functional testing and regression testing. Database access falls outside the scope of unit testing, so you would not write unit tests that include database access.


1 Answers

The final solution that fixes my problem is to force Django to disable migration during testing, which can be done from the settings like this

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 None      MIGRATION_MODULES = DisableMigrations() 

or use https://pypi.python.org/pypi/django-test-without-migrations

My whole test now takes about 1 minute and a small app takes 5 seconds.

In my case, migrations are not needed for testing as I update tests as I migrate, and don't use migrations to add data. This won't work for everybody

like image 183
dkarchmer Avatar answered Sep 18 '22 22:09

dkarchmer