chrisbillows / raindrop-todoist-syncer

Converts favourited Raindrops into tasks in Todoist.
2 stars 1 forks source link

Implement full `.env` solution that facilitates easy mocking #48

Closed chrisbillows closed 6 months ago

chrisbillows commented 6 months ago

46 works but still feels like a piecemeal solution.

~Lets admit defeat, the shortcuts have failed, the shortcuts have taking far longer than the long cut ever would have!~

~Extend the EnvironmentVariablesFileManager to become a container for the imported values. An evfm object can be instantiated from the a file and then passed to all objects that require .env values. They will need to be updated to require an evfm object.~

~Tasks:~

chrisbillows commented 6 months ago

I have done some research and experimentation:

A confest.py fixture works in the terminal

@pytest.fixture(autouse=True)
def set_env_vars_confest(monkeypatch):
    monkeypatch.setenv('todoist_api', 'monkeypatch_conftest_todoist')
    monkeypatch.setenv('raindrop_api', 'monkeypatch_conftest_raindrop')

And can be overridden in a test file:

def test_picking_up_test_env(monkeypatch):
    monkeypatch.setenv('todoist_api', 'override conftest')
    expected = "_test-todoist"
    actual = todoist_api_getter()
    assert actual == expected

And can be overridden by another fixture passed to the test:

@pytest.fixture
def set_env_vars_in_test_file(monkeypatch):
    monkeypatch.setenv('todoist_api', 'monkeypatch_in_test_file')

def test_picking_up_this_file_fixture(set_env_vars_in_test_file):
    expected = "_monkeypatch_in_file_todoist"
    actual = todoist_api_getter()
    assert actual == expected

(This doesn't work to override:

def test_picking_up_env():
    # THIS DOES NOTHING HERE??
    from dotenv import load_dotenv
    load_dotenv("env.test", override=True)
    print("Loaded todoist_api from test - expect: test-todoist || got:", os.getenv("todoist_api"))
    expected = "_test_todoist"
    actual = todoist_api_getter()
    assert actual == expected

Two alternatives

Don't load them as environment variables

From the python-dotenv docs: load-configuration-without-altering-the-environment

Or this example from [I/O flood](https://ioflood.com/blog/python-dotenv-guide-how-to-use-environment-variables-in-python/#:~:text=Using%20the%20load_dotenv()%20Function,-Having%20set%20up&text=This%20function%20loads%20the%20variables,them%20accessible%20to%20our%20application.&text=In%20this%20script%2C%20'DATABASE_URL',env%20file):

from dotenv import dotenv_values

# Get a dictionary of .env variables
config = dotenv_values()

# Get 'SECRET_KEY'
secret_key = config.get('SECRET_KEY')

# Now you can use the secret_key variable in your application
print(f'Secret Key: {secret_key}')

Pass default values

We can consider this:

database_url = os.environ.get('DATABASE_URL', 'sqlite:///')
database_url
>>> 'sqlite:///'

See: Using os.environ.get()

The idea would be that the repo has no .env file so, during testing, the default values will all be picked up and can be reliably tested against.

Additional

chrisbillows commented 6 months ago

Fixed by #52 / #54