Integration tests still reading production credentials despite override_settings in Django
0 reputation · 04 May 2022, 02:07 UTC
0 reputation · 04 May 2022, 02:07 UTC
I want a reliable way to run Django integration tests that exercise code paths normally talking to external services (email, cache, payment or storage APIs) without ever loading production credentials. The project currently relies on environment variables for secrets, and the test suite uses override_settings in places, but there is no enforced project-wide convention for keeping real secrets out of test runs.
Some third-party apps appear to read configuration at module import time, so swapping settings after django.setup() may be too late to stop them from capturing real values. CI jobs may also export production secret variables into the same shell that runs tests, and libraries that resolve settings lazily could pick those up. The team is undecided between three approaches: a dedicated test settings module selected via DJANGO_SETTINGS_MODULE, per-test override_settings decorators, or built-in credential-free stand-ins such as the locmem email backend and dummy cache.
Assume a current Django LTS release; settings-loading and test-runner initialization details may differ across versions.
35275 reputation · 04 May 2022, 11:19 UTC
To guarantee that no module sees production secrets, you must use a separate settings module selected via DJANGO_SETTINGS_MODULE. Decorator-based overrides (@override_settings) are insufficient for this goal because they apply only during the execution of the decorated function.
override_settings Fails for SecretsMany third-party libraries and some Django AppConfig implementations read configuration at the module level during the initial import phase. By the time django.setup() is called and your tests begin executing, these libraries have already captured the production values from the environment or the default settings file. Because override_settings modifies the settings object at runtime, it cannot "undo" a value that has already been cached in a third-party module's internal state.
settings_test.py: Define all secrets as dummy values or use built-in stand-ins (e.g., EMAIL_BACKEND = 'django.core.mail.backends.locmem.LocMemEmailBackend' and CACHES = {'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}}).export DJANGO_SETTINGS_MODULE=myproject.settings_test
python manage.py testunset PROD_API_KEY PROD_DB_PASSWORD && python manage.py testTo verify that no real credentials or network connections are being used, implement the following checks:
os.environ does not contain production keys.responses or httpretty to intercept all outbound HTTP requests. If a test attempts to hit a production API endpoint, the interceptor will trigger a failure.Are any of your third-party libraries calling os.environ.get() directly instead of accessing django.conf.settings? If so, changing the settings module will not be enough; you must explicitly unset those variables in the shell before running tests.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.