Parameterizing Django Settings with Pytest Fixtures

July 10, 2023

I'm currently working on a Django Rest Framework project and needed a way to test how my code behaves under different settings configurations. The pytest-django docs mention a settings fixture, which seemed like the right tool for the job.

The question was: how do I parameterize this settings fixture?

My first attempt

My initial approach was straightforward but wrong:

import pytest

@pytest.mark.parametrize("settings", [
    {"DEBUG": True},
    {"DEBUG": False}
])
def test_settings_debug(settings):
    print(settings)
    assert settings.DEBUG in (True, False)

This didn't work as expected. It replaced the settings fixture (which is of type pytest_django.fixtures.SettingsWrapper) with a plain dict. Not what I wanted.

The solution

I needed a way to parameterize settings without losing the fixture's functionality. Here's what worked:

@pytest.fixture()
def custom_settings():
    return {}

@pytest.fixture(autouse=True)
def override_django_settings(custom_settings, settings):
    for k, v in custom_settings.items():
        setattr(settings, k, v)

@pytest.mark.parametrize("custom_settings", [
    {"DEBUG": True},
    {"DEBUG": False}
])
def test_settings_debug(settings):
    print(settings)
    assert settings.DEBUG in (True, False)

How it works

The solution uses two fixtures:

custom_settings: This fixture returns an empty dictionary by default. When parameterized, it provides the settings you want to override without directly touching the settings fixture.

override_django_settings: This fixture runs automatically for every test (thanks to autouse=True). It takes whatever custom_settings provides and applies those values to the actual settings fixture using setattr.

For more on pytest fixtures and parameterization, check out the pytest documentation.