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.
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()).'