François Constant logo

Faster Django Tests

July 2016

Speed is a feature. When it comes to automated tests, speed is critical. A faster test suite means that your team is more likely to run the tests often, to enjoy the fast feedback and to therefore write more tests and write them first.

The easiest way to implement tests in Django is to write functional tests - i.e. tests that uses files, a database, fixtures, etc. These tests can become “slow”. Here are my tricks to speed your test suite.

1.Take advantage of your hardware

From Django 1.9, you can run tests in parallel. This is an excellent reason to upgrade to Django 1.9 !

./manage.py test my_app.tests --parallel

2.Make sure to mock APIs

This is obvious, you don’t want your tests to depend on your Internet connection. It’s obvious but it takes one clever developer to write some cached call somewhere in your application to ruin your tests. If that caching doesn’t happen in your tests (the database is constantly cleared), every single functional test will hit an API somewhere in the cloud !

So basically, make sure your test can run offline; switch off you wifi, unplug your ethernet cable and run the tests.

3.Disable migrations

When you develop large applications - or when you use plugins like FeinCMS that creates tons of database tables, the initial migrate script might take long. To disable migrations, simply add the following code to your settings:

TESTING = False

if len(sys.argv) >= 2:
    if sys.argv[0].endswith('manage.py'):
        if sys.argv[1] == 'test':
            TESTING = True

if TESTING:
    class DisableMigrations(object):
        """ no migrations to run the tests fast """
        def __contains__(self, item):
            return True

        def __getitem__(self, item):
            return "no migrations"

MIGRATION_MODULES = DisableMigrations()

4.Disable debugging tools

You don’t need any debugging in your tests, remove it and don’t forget plugins such as SORL and the infamous Django Debug Toolbar.

DEBUG = False
TEMPLATE_DEBUG = False
DEBUG_LOGGING = False

# for sorl:
THUMBNAIL_DEBUG = False

# removing Django Debug Toolbar
INSTALLED_APPS = [app for app in INSTALLED_APPS if app not in ('debug_toolbar',)]
MIDDLEWARE_CLASSES = [cls for cls in MIDDLEWARE_CLASSES
                      if cls not in (
                          'debug_toolbar.middleware.DebugToolbarMiddleware',
                      )]

5.Switch to SQLite

This is really easy to do and can offer a dramatic speed boost. Proceed with caution, you might want to run your test suit against Postgres once in a while to make sure you catch Integrity constraints.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': ':memory:'
    }
}

6.In memory emails

Chances are that you’re using “django.core.mail.backends.console.EmailBackend” in development. There is an even simpler/faster backend for your tests:

EMAIL_BACKEND = 'django.core.mail.backends.locmem.EmailBackend'

7.Simple password hasher

If your website involves user accounts, you’ll probably create lots of users in your tests. While running your tests you don’t need a sophisticated password hasher, you need speed !

PASSWORD_HASHERS = ('django.contrib.auth.hashers.MD5PasswordHasher',)

8.In memory files

If you upload files in your tests, you can gain significant speed by avoiding the bottle neck: the hard drive and using your RAM instead. To do so install Django In Memory storage and set it up:

DEFAULT_FILE_STORAGE = 'inmemorystorage.InMemoryStorage'

9.Do not use fixtures

This is probably the most important tip in this list. Using fixtures files that grow with your application not only make your tests harder to understand and update; that also dramatically slows down your test suite. The best alternative is to use factories .