django - how to detect test environment (check / determine if tests are being run)

DjangoUnit Testing

Django Problem Overview


How can I detect whether a view is being called in a test environment (e.g., from manage.py test)?

#pseudo_code
def my_view(request):
    if not request.is_secure() and not TEST_ENVIRONMENT:
        return HttpResponseForbidden()

Django Solutions


Solution 1 - Django

Put this in your settings.py:

import sys

TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test'

This tests whether the second commandline argument (after ./manage.py) was test. Then you can access this variable from other modules, like so:

from django.conf import settings

if settings.TESTING:
    ...

There are good reasons to do this: suppose you're accessing some backend service, other than Django's models and DB connections. Then you might need to know when to call the production service vs. the test service.

Solution 2 - Django

Create your own TestSuiteRunner subclass and change a setting or do whatever else you need to for the rest of your application. You specify the test runner in your settings:

TEST_RUNNER = 'your.project.MyTestSuiteRunner'

In general, you don't want to do this, but it works if you absolutely need it.

from django.conf import settings
from django.test.simple import DjangoTestSuiteRunner

class MyTestSuiteRunner(DjangoTestSuiteRunner):
    def __init__(self, *args, **kwargs):
        settings.IM_IN_TEST_MODE = True
        super(MyTestSuiteRunner, self).__init__(*args, **kwargs)

Solution 3 - Django

Just look at request.META['SERVER_NAME']

def my_view(request):
    if request.META['SERVER_NAME'] == "testserver":
        print "This is test environment!"

Solution 4 - Django

There's also a way to temporarily overwrite settings in a unit test in Django. This might be a easier/cleaner solution for certain cases.

You can do this inside a test:

with self.settings(MY_SETTING='my_value'):
    # test code

Or add it as a decorator on the test method:

@override_settings(MY_SETTING='my_value')
def test_my_test(self):
    # test code

You can also set the decorator for the whole test case class:

@override_settings(MY_SETTING='my_value')
class MyTestCase(TestCase):
    # test methods

For more info check the Django docs: https://docs.djangoproject.com/en/1.11/topics/testing/tools/#django.test.override_settings

Solution 5 - Django

I think the best approach is to run your tests using their own settings file (i.e. settings/tests.py). That file can look like this (the first line imports settings from a local.py settings file):

from local import *
TEST_MODE = True

Then do ducktyping to check if you are in test mode.

try:
    if settings.TEST_MODE:
        print 'foo'
except AttributeError:
    pass

Solution 6 - Django

If you are multiple settings file for different environment, all you need to do is to create one settings file for testing.

For instance, your setting files are:

your_project/
      |_ settings/
           |_ __init__.py
           |_ base.py  <-- your original settings
           |_ testing.py  <-- for testing only

In your testing.py, add a TESTING flag:

from .base import *

TESTING = True

In your application, you can access settings.TESTING to check if you're in testing environment.

To run tests, use:

python manage.py test --settings your_project.settings.testing

Solution 7 - Django

While there's no official way to see whether we're in a test environment, django actually leaves some clues for us. By default Django’s test runner automatically redirects all Django-sent email to a dummy outbox. This is accomplished by replacing EMAIL_BACKEND in a function called setup_test_environment, which in turn is called by a method of DiscoverRunner. So, we can check whether settings.EMAIL_BACKEND is set to 'django.core.mail.backends.locmem.EmailBackend'. That mean we're in a test environment.

A less hacky solution would be following the devs lead by adding our own setting by subclassing DisoverRunner and then overriding setup_test_environment method.

Solution 8 - Django

Piggybacking off of @Tobia's answer, I think it is better implemented in settings.py like this:

import sys
try:
    TESTING = 'test' == sys.argv[1]
except IndexError:
    TESTING = False

This will prevent it from catching things like ./manage.py loaddata test.json or ./manage.py i_am_not_running_a_test

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiontbackView Question on Stackoverflow
Solution 1 - DjangoTobiaView Answer on Stackoverflow
Solution 2 - DjangoTravis JensenView Answer on Stackoverflow
Solution 3 - DjangoTomasz KarbownickiView Answer on Stackoverflow
Solution 4 - DjangogitaarikView Answer on Stackoverflow
Solution 5 - DjangopymarcoView Answer on Stackoverflow
Solution 6 - DjangomelvinView Answer on Stackoverflow
Solution 7 - Djangosg2002View Answer on Stackoverflow
Solution 8 - DjangoCoryView Answer on Stackoverflow