pytest modules using os.environ - Ho do I test it correctly?

spky picture spky · Jun 18, 2015 · Viewed 10k times · Source

currently I am writing some Webapp, but this time I want to learn how to write proper tests for it (using pytest) :)

A very common pattern I often see is to make the default configuration changeable using environment variables. Currently I am struggling how to test this properly.

I've prepared some demo:

./app
./app/conf.py
./conftest.py
./run.py
./tests
./tests/test_demo.py

My ./app/conf.py looks like this:

from os import environ

DEMO = environ.get('DEMO', 'demo')
TEST = environ.get('TEST', 'test')

Launching the ./run.py shows that the settings are indeed changeable:

from os import environ

environ['DEMO'] = 'not a demo'
environ['TEST'] = 'untested'

from app import conf

if __name__ == '__main__':

    print(conf.DEMO)
    print(conf.TEST)

It prints out not a demo and untested - as expected. Great. (Note that I set the environment variables before importing conf).

Now to the tests: The ./conftest.py is currently empty it just helps pytest to locate the modules inside the app folder.

The ./tests/test_demo.py contains the following:

def test_conf_defaults():
    from app import conf

    assert conf.DEMO == 'demo'
    assert conf.TEST == 'test'


def test_conf_changed(monkeypatch):
    monkeypatch.setenv('DEMO', 'tested demo')
    monkeypatch.setenv('TEST', 'demo test')

    from app import conf

    assert conf.DEMO == 'tested demo'
    assert conf.TEST == 'demo test'

    monkeypatch.undo()

If I run pytest now, test_conf_changed fails with 'demo' == 'tested demo' -> the monkeypatch function did not patch the environment.

If I swap both testing functions (so test_conf_changed runs first), the test_conf_defaults will fail with 'tested demo' == 'demo'.

How I interpret it, is - the first time conf gets imported it sticks there with it's initial settings..

How can I tell pytest to completely reimport conf each test function, after setting up the environment variables?

I am stuck there for two days now - and slowly I doubt if testing is worth the hassle - please prove me wrong :)

Answer

spky picture spky · Jun 18, 2015

Thanks for the hint, Evert (variables inside the conf module are set inside the global namespace, they stick around) - I think I got it now.

To test my code I have to explicitly reimport conf after setting the environment variables. Changing the code in ./tests/test_demo.py to this does the trick:

from importlib import reload

from app import conf


def test_conf_changed(monkeypatch):
    monkeypatch.setenv('DEMO', 'tested demo')
    monkeypatch.setenv('TEST', 'demo test')

    reload(conf)

    assert conf.DEMO == 'tested demo'
    assert conf.TEST == 'demo test'


def test_conf_defaults():

    reload(conf)

    assert conf.DEMO == 'demo'
    assert conf.TEST == 'test'

Thank you.