Rollback Multiple Test Databases in Django with Pytest

Adding multi-database rollback support to pytest-django using fixtures that patch Django's test classes.

By Merit Open Source and Technology on July 05, 2021

UPDATE: 2021-05-10

The pytest-django team is actively working on resolving this issue! 🎉 Nearly the same day we put this up they started merging duplicate tickets and even have an initial PR!

View/track progress at:

Until an official solution lands feel free to follow this article. Once there’s an official solution we would strongly encourage using it!

What’s the problem?

Django, pytest, and pytest-django are awesome and work very well together when you have a single database and/or use a single, temporary test database that is created and destroyed every test run.

If you have multiple (test) databases that persist before/after test runs you may find that your test data remains for all databases that aren’t named “default”.

This can lead to all sorts of issues with tests and isn’t great when you have shared test databases.

pytest-django doesn’t support multi-db rollback

When setting up the database fixtures (db, transactional_db) pytest-django makes use of TransactionTestCase or TestCase. Both of these use transactions under the hood and while they may also do things atomically the big difference is that one truncates all tables while the other resets state after each test.

The challenge in using these with pytest-django is that TransactionTestCase and TestCase are hard-coded to only work with the default database via DEFAULT_DB_ALIAS (see this at https://github.com/django/django/blob/main/django/test/testcases.py#L927).

Because of the hard-coded DEFAULT_DB_ALIAS any database that isn’t the default won’t have rollback and will persist any changes made during tests.

This is less an issue with pytest-django and more an issue with hard-coded defaults in Django’s test classes. Django expects you to provide the DB names for test cases as-needed in a multi-database test setup (see https://docs.djangoproject.com/en/3.2/topics/testing/tools/#testing-multi-db) but because we aren’t directly sub-classing test cases we can’t specify our databases.

Using pytest fixtures to patch Django’s test classes

The fix for this is pretty simple! We’re going to use a pytest fixture that’ll patch Django’s test classes to support all databases defined in Django’s settings.

Assuming you already have pytest and pytest-django setup and working this is all you need to add to your base conftest.py:

import pytest


@pytest.fixture(autouse=True, scope='session')
def django_db_multiple():
    """
    Ensure all test functions using Django test cases have
    multiple database rollback support.
    """
    from _pytest.monkeypatch import MonkeyPatch
    from django.conf import settings
    from django.test import TestCase
    from django.test import TransactionTestCase

    db_keys = set(settings.DATABASES.keys())

    monkeypatch = MonkeyPatch()
    monkeypatch.setattr(TestCase, 'databases', db_keys)
    monkeypatch.setattr(TransactionTestCase, 'databases', db_keys)

    yield monkeypatch

    monkeypatch.undo()

All set! You now have rollback support for all databases. You don’t need to do anything else and can get back to writing tests. Any changes you make in your tests will be rolled back across all of your databases.

This works by replacing the hard-coded default DEFAULT_DB_ALIAS with the database names defined in your settings. Because it’s an autouse, session-scoped fixture in your base configuration it’ll be run once automatically and persist for all tests.

Other solutions and discussions

There isn’t yet a consensus on how best to do this and there isn’t yet an official solution. What we’re using is based on comments and direction from contributors to pytest-django but there’s always more than one way to do things.

For alternative solutions and more discussion:

Have a better way? Disagree? Let us know!



Merit Network, Inc.

A nonprofit member-governed organization providing high-performance computer networking and related services to educational, government, health care, and nonprofit organizations, primarily in Michigan.


Connect with us: