If you use Test Driven Development or test your code regularly during development, running your test suite can start to become very time consuming. Here’s some tips for speeding up your tests. All of these changes can go in a test_settings.py file, which your can import into your settings.py file only when a test is being run.
- Use Sqlite
I’ve written about this before. When using Sqlite, the Django test runner will create the DB in memory instead of on disk, which is faster than any other database available.
- Skip South Migrations
If you’re using South, migrations will be run during the set up for every test, which can add a lot of time to your tests. Set “SOUTH_TESTS_MIGRATE = False” to use the normal syndb behaviour for tests.
- Use an In-Memory Storage Class
There’s a project called django-inmemorystorage, which stores files in memory instead of on disk. This is not only faster but eliminates the problem of having to clean up files created during testing. Unfortunately, it doesn’t currently support the url() method, so using the “.url” attribute of a FileField or ImageField will throw an error, which is why I created a fork to solve this issue. After installing set “DEFAULT_FILE_STORAGE = ‘inmemorystorage.InMemoryStorage’” in your test settings.
- Use Weaker Password Hashing
Prior to Django 1.4, Django used SHA1 to hash passwords. Starting with 1.4, Django can be configured to use a number of password hashers. The default setting is:
PASSWORD_HASHERS = ( 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.BCryptPasswordHasher', 'django.contrib.auth.hashers.SHA1PasswordHasher', 'django.contrib.auth.hashers.MD5PasswordHasher', 'django.contrib.auth.hashers.CryptPasswordHasher', )
Django will encrypt passwords using the first one in this list, PBKDF2PasswordHasher, which was designed to take a really long time to execute in order to be harder to break. This is an important security feature that should be left in place on your live site, but for running a test suite it’s really not needed. You can set it to the following in order to go back to using SHA1:
PASSWORD_HASHERS = ( 'django.contrib.auth.hashers.SHA1PasswordHasher', 'django.contrib.auth.hashers.PBKDF2PasswordHasher', 'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher', 'django.contrib.auth.hashers.BCryptPasswordHasher', 'django.contrib.auth.hashers.MD5PasswordHasher', 'django.contrib.auth.hashers.CryptPasswordHasher', )
I have one project whose tests took 50 seconds to complete before I made this change, and 7 seconds afterwards.
- Mock Network Calls
Mocking network calls is not only a best practice, but is also a lot faster than making real requests. I recommend using Mock. It gets the job done, it’s widely used, and it’s even being merged into the standard library of Python 3.3. Here’s a condensed example of how to use it:
Please note, aside from using mock objects, these techniques should only be used while testing during development. On a regular basis (such as nightly, or after every commit on a CI server, and/or before pushing your changes to your code repo), you should run your test suite using settings as close to your live site settings as possible (same DB type, same storage, run migrations, etc.) in order to weed out any bugs.
If you have any tips feel free to share!