Search code examples
pythonpytest

How do I set environment variables in a pytest fixture with the MonkeyPatch context manager?


I'm not using classes or test cases I'm just using pytest functions (want to keep it that way).

This does not work:

@pytest.fixture(scope="function")
def set_env():
    with MonkeyPatch.context() as mp:
        mp.setenv("VAR_ONE", "123")
        mp.setenv("VAR_TWO", "test")

def test_blah(set_env):
    print(os.environ["VAR_ONE"])
    print(os.environ["VAR_TWO"])

This does:

@pytest.fixture(scope="function")
def set_env(monkeypatch):
    monkeypatch.setenv("VAR_ONE", "123")
    monkeypatch.setenv("VAR_TWO", "test")

def test_blah(monkeypatch, set_env):
    print(os.environ["VAR_ONE"])
    print(os.environ["VAR_TWO"])

I was hoping to avoid passing around monkeypatch fixtures like this. I thought I could use MonkeyPatch to abstract this behind a single fixture. Am I misunderstanding MonkeyPatch as a context manager?

The whole pytest fixture magic thing doesn't play nice with type hinting so I really want to minimize the fixtures I need to pass around (while still not using test cases and classes).


Solution

  • You only need context to do and undo changes for a limited time within a single function. Otherwise, the scope of the patch is limited by the scope of the fixture using monkeypatch.

    From the documentation:

    All modifications will be undone after the requesting test function or fixture has finished. The raising parameter determines if a KeyError or AttributeError will be raised if the set/deletion operation does not have the specified target.

    To undo modifications done by the fixture in a contained scope, use context().

    import pytest, os
    
    @pytest.fixture(scope="function")
    def set_env(monkeypatch):
        monkeypatch.setenv("VAR_ONE", "123")
        monkeypatch.setenv("VAR_TWO", "test")
    
    def test_one(set_env):
        assert os.environ["VAR_ONE"] == "123"
        assert os.environ["VAR_TWO"] == "test"
    
    def test_two(monkeypatch):
        assert "VAR_ONE" not in os.environ
        with monkeypatch.context() as mp:
            mp.setenv("VAR_ONE", "123")
            assert os.environ["VAR_ONE"] == "123"
        assert "VAR_ONE" not in os.environ