Speed up your test suite

Django test settings for speed (debug toolbar, DEBUG, static storage)

Development settings slow Django tests: keep django-debug-toolbar out, don't force DEBUG = True, and use StaticFilesStorage instead of manifest or WhiteNoise storage.

Most Django projects run their tests with the settings they use for development, plus a few overrides. Some of what's useful while developing costs time in every test: django-debug-toolbar instrumenting requests, DEBUG = True recording every SQL query, and a manifest static files storage looking up hashed file names for every {% static %} tag. None of it makes the tests more meaningful.

This guide covers those three. The other settings worth changing for tests have their own guides, listed at the end.

A TESTING flag

Settings files need to know when they're loaded for tests. django-debug-toolbar's documentation uses:

# settings.py
import os
import sys

TESTING = "test" in sys.argv or "PYTEST_VERSION" in os.environ

manage.py test puts test in sys.argv, and pytest 8.2+ sets PYTEST_VERSION. If you keep a separate test settings module instead, put the overrides there.

Keep django-debug-toolbar out of tests

When the toolbar is active for a request, every panel instruments it: the SQL panel wraps the database cursor and records each query with a stack trace, others record templates and their context, cache calls and signals, and the result is rendered into the response. That's useful in a browser and pure overhead in a test.

The toolbar is active when DEBUG is on (or your SHOW_TOOLBAR_CALLBACK says so) and the request comes from INTERNAL_IPS. The test client's requests come from 127.0.0.1, which is usually in that list. Django's test runner turns DEBUG off, but only after the settings module has run, so a block like if DEBUG: INSTALLED_APPS += ["debug_toolbar"] still installs it.

Install it only when not testing, as the toolbar's documentation recommends:

# settings.py
if DEBUG and not TESTING:
    INSTALLED_APPS = [*INSTALLED_APPS, "debug_toolbar"]
    MIDDLEWARE = ["debug_toolbar.middleware.DebugToolbarMiddleware", *MIDDLEWARE]
# urls.py
from django.conf import settings

if settings.DEBUG and not settings.TESTING:
    from debug_toolbar.toolbar import debug_toolbar_urls

    urlpatterns = [*urlpatterns, *debug_toolbar_urls()]

Recent versions of the toolbar also have a system check (debug_toolbar.E001) that fails when it detects tests running with a toolbar setup that would break them.

Don't run tests with DEBUG = True

Django's test runner sets DEBUG = False for the run regardless of your settings, and so does pytest-django. Tests only run with DEBUG on when something forces it:

  • python manage.py test --debug-mode
  • django_debug_mode = true in pytest-django's configuration, or keep with DEBUG = True in settings
  • override_settings(DEBUG=True) on a base test class, or a fixture that sets settings.DEBUG = True

With DEBUG on, Django wraps every database cursor in CursorDebugWrapper, which times each query, renders the SQL with its parameters and appends it to connection.queries (up to 9,000 entries per connection). Error responses render the technical debug page, and other code that checks DEBUG behaves the way it does on your laptop, not in production.

Remove whatever forces it. In pytest-django the default is already right:

# pyproject.toml
[tool.pytest.ini_options]
django_debug_mode = false

Tests that need debug behaviour can turn it on for themselves with @override_settings(DEBUG=True). Tests that want to look at queries don't need DEBUG at all: assertNumQueries, django_assert_num_queries and django.test.utils.CaptureQueriesContext enable query recording just inside their block.

Use plain static file storage in tests

ManifestStaticFilesStorage, and WhiteNoise's CompressedManifestStaticFilesStorage built on it, map every {% static "app.css" %} to a hashed name like app.3f2a1c.css using the staticfiles.json manifest that collectstatic writes. In tests that means:

  • you have to run collectstatic before the tests, a slow CI step, or templates fail with ValueError: Missing staticfiles manifest entry
  • every {% static %} tag goes through the manifest lookup

WhiteNoise's middleware adds its own cost: when it's created with DEBUG off it walks STATIC_ROOT and records every file. Django's test client builds the middleware chain again for every new client, and every test gets a new client, so with a populated STATIC_ROOT that scan happens in each test that makes a request.

Django's documentation says this storage typically shouldn't be used when running tests. Use the default storage in test settings:

# settings.py
if TESTING:
    STORAGES = {
        **STORAGES,
        "staticfiles": {"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage"},
    }
    MIDDLEWARE = [m for m in MIDDLEWARE if m != "whitenoise.middleware.WhiteNoiseMiddleware"]

If your settings don't define STORAGES at all, you already have StaticFilesStorage, Django's default. Removing the WhiteNoise middleware is only safe if no test requests a static file through Django; most don't. Your test job no longer needs collectstatic either.

The other settings for fast tests

Setting In tests Guide
PASSWORD_HASHERS MD5PasswordHasher fast password hasher
CACHES LocMemCache instead of Redis or Memcached in-memory cache
CELERY_TASK_ALWAYS_EAGER True, with CELERY_TASK_EAGER_PROPAGATES eager Celery tasks
LOGGING WARNING and above, no debug handlers logging in tests

EMAIL_BACKEND needs nothing: Django's test runner and pytest-django already switch it to the in-memory backend.

Keep the tests correct

  • Change these in test settings only. Production keeps its manifest storage and DEBUG = False.
  • Tests that rely on DEBUG behaviour (error pages, debug-only views) can override it with override_settings on that test.
  • With StaticFilesStorage, a {% static %} tag naming a missing file no longer fails a test. Keep collectstatic --noinput in your build or deploy pipeline, which still fails on broken references in CSS, and keep one job with the manifest storage if you rely on tests to catch missing static files.

How flakytest finds it

flakytest reads your Django settings during the run and measures the time in the code involved:

  • Disable django-debug-toolbar in tests: debug_toolbar is in INSTALLED_APPS and its code takes at least 0.2 seconds.
  • Tests run with DEBUG = True: DEBUG is on during the tests and at least 0.3 seconds is spent in CursorDebugWrapper.
  • Use plain static file storage in tests: Django's staticfiles storage or WhiteNoise takes at least half a second.

Further reading

Related guides

  • Use a fast password hasher in tests: Django hashes passwords with hundreds of thousands of PBKDF2 iterations, and every create_user and client.login in a test pays for it. Switch to a fast hasher in test settings and use force_login.
  • Use an in-memory cache in tests: Tests that talk to a real Redis or Memcached pay a network round trip per cache call and share state between tests. Use Django's LocMemCache or fakeredis in tests and clear it per test.
  • Run Celery tasks eagerly in tests: Tests that call task.delay() publish to a broker, retry against a missing one or wait on a worker. Run tasks eagerly with CELERY_TASK_ALWAYS_EAGER, or mock delay.
  • Logging is expensive in tests: DEBUG logging in tests creates, formats and captures a record for every log call. Raise the log level in test settings, drop extra handlers, use caplog only where needed.

Updated October 18, 2026 · Suggest a change