Open-Source Wikis

/

Django

/

How to contribute

/

Testing

django/django

Testing

Django's test suite is the project's central artifact. It's larger than the framework itself and is the canonical executable spec for almost every public behaviour.

The test runner

Tests run via tests/runtests.py, a thin wrapper around django.test.runner.DiscoverRunner (in django/test/runner.py). The runner discovers test apps in the tests/ directory, configures a settings module, and dispatches.

cd tests
python runtests.py                     # all tests, default backend (SQLite)
python runtests.py model_fields        # one test app
python runtests.py model_fields.tests.IntegerFieldTests
python runtests.py --parallel 4
python runtests.py --keepdb            # don't drop the test DB between runs
python runtests.py --verbosity 2
python runtests.py --debug-sql         # log SQL on failure

--keepdb is the most useful flag during development: setting up the test database costs ~10 seconds, dropping and recreating it for every run is wasteful.

Settings modules

The default settings file is tests/test_sqlite.py. To run against a different backend:

python runtests.py --settings test_postgres
python runtests.py --settings test_mysql
python runtests.py --settings test_oracle

The other settings files at the top of tests/ (e.g., test_runner_apps/, test_postgres.py) define DATABASES for each backend. You'll need a local database server and the matching Python driver (psycopg, mysqlclient, oracledb).

For GIS tests, tests/gis_tests/ requires GEOS, GDAL, and PROJ to be installed system-wide. If they're not present, the test app skips gracefully.

Test classes

Tests inherit from django.test.TestCase (or one of its siblings), defined in django/test/testcases.py:

Class Behaviour
SimpleTestCase No DB, no transactions. Fastest.
TransactionTestCase Each test runs in its own transaction; truncates between tests. Slower, but supports raw SQL.
TestCase Wraps each test in a transaction that gets rolled back. The default.
LiveServerTestCase Spins up a live server in a thread; for end-to-end tests with Selenium, etc.
StaticLiveServerTestCase Like LiveServerTestCase but serves STATIC_URL from the staticfiles storage.

Most subsystem tests use plain TestCase and lean on its transaction rollback for isolation. The exceptions are tests that exercise TRUNCATE-like behaviour, multi-database tests, and tests that explicitly span connections.

Test client

django.test.client.Client (in django/test/client.py) is a fake HTTP client that bypasses the network and dispatches directly to the WSGI handler. It exposes the standard verbs (get, post, put, patch, delete, head, options) plus async variants on AsyncClient.

def test_homepage(self):
    response = self.client.get("/")
    self.assertEqual(response.status_code, 200)
    self.assertContains(response, "Welcome")

Fixtures

Fixtures live in tests/<app>/fixtures/ and are loaded with fixtures = ["fixture_name.json"] on the test class. They're loaded inside the test transaction. Newer test code prefers setUpTestData (a class method that creates objects once for all test methods) over JSON fixtures because Python is faster than JSON loading.

Running a subset

The runtests CLI accepts dotted paths down to the method level:

python runtests.py queries
python runtests.py queries.tests.QueriesTests
python runtests.py queries.tests.QueriesTests.test_basic

-k <pattern> filters by test name substring (passed through to unittest):

python runtests.py queries -k subquery

Parallelisation

--parallel <N> forks N worker processes; each gets its own copy of the test database. Defaults to using all CPUs on POSIX systems. The runner re-merges results at the end. Some tests opt out of parallelism via the serialized_rollback flag or by using TransactionTestCase.

Async tests

Async tests use unittest.IsolatedAsyncioTestCase or Django's django.test.testcases.TransactionTestCase with async def methods. The handler bridge in django/test/client.py supports AsyncClient for async views.

Mocking and patching

Django uses standard library unittest.mock extensively. The framework also exposes a few helpers:

  • override_settings (django/test/utils.py) — temporarily swap settings for a test.
  • modify_settings — append/prepend list-valued settings.
  • captured_stdout / captured_stderr — capture stream output.
  • isolate_apps — register temporary apps for a test method.
  • CaptureQueriesContext — collect all SQL executed in a block.

Coverage

The project does not enforce coverage thresholds. coverage run -p tests/runtests.py works if you want to measure locally.

Continuous integration

.github/workflows/ contains the CI matrix. Pushes to main and PRs run the full suite across SQLite + PostgreSQL on supported Python versions, plus black/flake8/isort/docs/lint-docs/zizmor. A separate workflow runs the JavaScript tests in js_tests/ against qunit.

Where to start reading

If you want a feel for how Django tests its own code, three good test apps to read:

  • tests/queries/ — exhaustive ORM queryset coverage.
  • tests/migrations/ — autodetector and graph tests with hundreds of model fixtures.
  • tests/admin_views/ — end-to-end admin tests that exercise URL routing, templates, forms, and the auth integration in concert.

Local QA screenshots

These screenshots came from local QA runs and are kept here as supporting test evidence rather than overview material.

Django admin anonymous login page

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

Testing – Django wiki | Factory