django setting environment variables in unittest tests

Question:

I want to be able to set environment variables in my Django app for tests to be able to run. For instance, my views rely on several API keys.

There are ways to override settings during testing, but I don’t want them defined in settings.py as that is a security issue.

I’ve tried in my setup function to set these environment variables, but that doesn’t work to give the Django application the values.

class MyTests(TestCase):
    def setUp(self):
        os.environ['TEST'] = '123'  # doesn't propogate to app

When I test locally, I simply have an .env file I run with

foreman start -e .env web

which supplies os.environ with values. But in Django’s unittest.TestCase it does not have a way (that I know) to set that.

How can I get around this?

Asked By: lollercoaster

||

Answers:

I use py.test as my test runner, and it allows you to create a pytest.ini file in which you can specify a particular settings file to use while running tests.

See documentation on this here:

http://pytest-django.readthedocs.org/en/latest/configuring_django.html#pytest-ini-settings

I recommend py.test in general as a test runner, because it supports different types of test classes and even simple functions, and it’s pretty easy to set up fixtures or other code that runs before and after tests.

Answered By: erewok

As @schillingt noted in the comments, EnvironmentVarGuard was the correct way.

from test.test_support import EnvironmentVarGuard # Python(2.7 < 3)
from test.support import EnvironmentVarGuard # Python >=3
from django.test import TestCase

class MyTestCase(TestCase):
    def setUp(self):
        self.env = EnvironmentVarGuard()
        self.env.set('VAR', 'value')

    def test_something(self):
        with self.env:
            # ... perform tests here ... #
            pass

This correctly sets environment variables for the duration of the context object with statement.

Answered By: lollercoaster

Old question, but it turned up in a Google search and neither of the existing answers are suitable. If you’re using pytest, env vars can be set/restored using pytest’s monkeypatching functionality.

Answered By: Tom

The test.support.EnvironmentVarGuard is an internal API that might be changed from version to version with breaking (backward incompatible) changes. In fact, the entire test package is internal use only. It was explicitly stated on the test package documentation page that it’s for internal testing of core libraries and NOT a public API. (see links below)

You should use patch.dict() in python’s standard lib unittest.mock. It can be used as a context manager, decorator or class decorator. See example code below copied from the official Python documentation.

import os
from unittest.mock import patch
with patch.dict('os.environ', {'newkey': 'newvalue'}):
    print(os.environ['newkey'])  # should print out 'newvalue'
    assert 'newkey' in os.environ  # should be True
assert 'newkey' not in os.environ  # should be True

Update: for those who doesn’t read the documentation thoroughly and might have missed the note, read more test package notes at

https://docs.python.org/2/library/test.html or

https://docs.python.org/3/library/test.html

Answered By: Devy

If you are loading your environment variables in Django’s settings.py file like this:

import os
ENV_NAME = os.environ.get('ENV_NAME', 'default')

You could use this:

from django.test import TestCase, override_settings

@override_settings(ENV_NAME="super_setting")
def test_...(self):
Answered By: Lucas03

Initially, my env variable PARTNER_CODE was set to wow.

I could able to change the env variable using the following:

from test.support import EnvironmentVarGuard
with EnvironmentVarGuard() as env:
   env['PARTNER_CODE'] = 'sos'

Now my env variable PARTNER_CODE says sos.

Answered By: Sekhar

Using EnvironmentVarGuard is not a good solution as it fails in some environments and works in others. see example below.

Python3.6 environment on gitlab ci

A better solution is what was suggested by erewok that requires making use of the unittest.mock in python3.

Assuming using unittest

from unittest.mock import patch
class TestCase(unittest.TestCase):

    def setUp(self):
        self.env = patch.dict('os.environ', {'hello':'world'})

    def test_scenario_1(self):
        with self.env:
            self.assertEqual(os.environ.get('hello'), 'world')

“`

Answered By: gbozee

This article explains all, the following snippet worked for me
How to Mock Environment Variables in Python’s unittest

      import os
    from unittest import TestCase, mock

class SettingsTests(TestCase):
    @classmethod
    def setUpClass(cls):
        cls.env_patcher = mock.patch.dict(os.environ, {"FROBNICATION_COLOUR": "ROUGE"})
        cls.env_patcher.start()

        super().setUpClass()

    @classmethod
    def tearDownClass(cls):
        super().tearDownClass()

        cls.env_patcher.stop()

    def setUp(self):
        super().setUp()
        self.assertEqual(os.environ["FROBNICATION_COLOUR"], "ROUGE")

    def test_frobnication_colour(self):
        self.assertEqual(os.environ["FROBNICATION_COLOUR"], "ROUGE")
Answered By: webjockey

Instructions all the way Mock Environment Variables in Python’s unittest https://adamj.eu/tech/2020/10/13/how-to-mock-environment-variables-with-pythons-unittest/

Answered By: cảnh nguyễn