Why are environment variables not being overridden in pytest?

248 views Asked by At

I'm writing some unit tests for a basic flask app. Currently I'm testing the config.py file (mainly to learn, but also to test an environment dependent database configuration).

The file looks like:

config.py

from os import environ as env
from dotenv import load_dotenv

load_dotenv()


class Config:
    ENVIRONMENT = env['CONFIG_MODE']
    SQLALCHEMY_TRACK_MODIFICATIONS = True
    SECRET_KEY = env['SECRET_KEY']

    # app deployed on Heroku - heroku sets DATABASE_URL to postgres://, SQLALCHEMY needs postgresql://
    if ENVIRONMENT == "development":
        SQLALCHEMY_DATABASE_URI = env['DEVELOPMENT_DATABASE_URL']
    else:
        SQLALCHEMY_DATABASE_URI = env.get('DATABASE_URL').replace("://", "ql://", 1)

This pulls environment variables from the .env file

.env

CONFIG_MODE = 'development'
DEVELOPMENT_DATABASE_URL = 'postgresql://usr:pwd@localhost:5432/db'

FLASK_APP=app
SECRET_KEY='XXX'

I've set up a basic pytest unit test to check that the SQLALCHEMY_DATABASE_URL is set correctly, depending on whether the CONFIG_MODE env variable is 'development' or 'not development'.

test_config.py

import os


class TestConfigDev:
    development_database_url = 'development_database_url'
    os.environ['DEVELOPMENT_DATABASE_URL'] = development_database_url
    os.environ['CONFIG_MODE'] = 'development'
    from service_authentication.api.config import Config
    config = Config

    def test_sqlalchemy_track_modifications(self):
        """
        GIVEN: SQLALCHEMY_TRACK_MODIFICATIONS variable exists
        WHEN: it is queried
        THEN: it is True
        """
        self.sqlalchemy_track_modifications = self.config.SQLALCHEMY_TRACK_MODIFICATIONS
        assert self.sqlalchemy_track_modifications

    def test_sqlalchemy_database_uri_dev(self):
        """
        GIVEN: CONFIG_MODE is development
        AND GIVEN: DEVELOPMENT_DATABASE_URL is set
        AND GIVEN: SQLALCHEMY_DATABASE_URI variable exists
        WHEN: it is queried
        THEN: it returns the DEVELOPMENT_DATABASE_URL
        """
        self.sqlalchemy_database_uri = self.config.SQLALCHEMY_DATABASE_URI
        assert self.sqlalchemy_database_uri == self.development_database_url


class TestConfigNotDev:
    database_url = 'postgres://database_url'
    os.environ['DATABASE_URL'] = database_url
    os.environ['CONFIG_MODE'] = 'not_development'
    from service_authentication.api.config import Config
    config = Config

    def test_sqlalchemy_database_uri_not_dev(self):
        """
        GIVEN: CONFIG_MODE is not development
        AND GIVEN: DATABASE_URL is set
        AND GIVEN: SQLALCHEMY_DATABASE_URI variable exists
        WHEN: it is queried
        THEN: it returns the modified DATABASE_URL
        """
        self.sqlalchemy_database_uri = self.config.SQLALCHEMY_DATABASE_URI
        self.database_url = self.database_url.replace("://", "ql://", 1)
        assert self.config.ENVIRONMENT == 'not_development'
        assert self.sqlalchemy_database_uri == self.database_url

The TestConfigDev tests pass with no issues.

The TestConfigNotDev tests fail - the ENVIRONMENT variable is still set to 'development', despite being overridden to 'not_development' in the Class definition.

I've removed CONFIG_MODE as a variable set in .env - isolating its instantiation to the test_config.py file. The issue persisted.

I've tried separating these out into two test files (test_config_dev.py and test_config_not_dev.py). Whichever runs first sets the ENVIRONMENT variable.

Thinking that the issue is that the env variables are set when import os is run, I've tried using importlib.reload(os) in the TestConfigNotDev class, before and after overriding the env variables, but it's still failing because the ENVIRONMENT variable remains as 'development'.

Curiously, the DEVELOPMENT_DATABASE_URL variable is being overridden in TestConfigDev.

This means that something else is going on with the way the environment variables are being set that I'm overlooking. Given how fundamental env variables are, I'd like to understand what's going on here before I go too much further.

1

There are 1 answers

0
741852963 On BEST ANSWER

From MrBean Bremen in the comments:

'You are not changing the environment variables during the test, but at load time. You need to set them in a class-scoped fixture (probably using monkeypatch.setenv()).'