Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Monkeypatch setenv value persist across test cases in python unittest

I have to set different environment variables for my test cases.

My assumption was once the test case is completed, monkeypatch will remove the env variables from os.environ. But it is not. How to set and revert the environment variables for each test?

Here is my simplified test case code with monkeypatch lib.

import os
import unittest
import time
from _pytest.monkeypatch import MonkeyPatch

class Test_Monkey_Patch_Env(unittest.TestCase):
    def setUp(self):
        print("Setup")

    def test_1(self):
        monkeypatch = MonkeyPatch()
        monkeypatch.setenv("TESTVAR1", "This env value is persistent")

    def test_2(self):
        # I was expected the env TESTVAR1 set in test_1 using monkeypatch
        # should not persist across test cases. But it is.
        print(os.environ["TESTVAR1"]) 

    def tearDown(self):
        print("tearDown")
            

if __name__ == '__main__':
    unittest.main()

Output:

Setup
tearDown
.Setup
This env value is persistent
tearDown
.
----------------------------------------------------------------------
Ran 2 tests in 0.001s

OK
like image 526
BEPP Avatar asked Nov 16 '25 17:11

BEPP


2 Answers

This expands a bit on the (correct) answer given by @MaNKuR.

The reason why it does not work as expected is that in pytest, it is not designed to be used this way - instead the monkeypatch fixture is used, which does the cleanup on leaving the test scope. To do the cleanup in unittest, you can do that cleanup in tearDown, as shown in the answer - though I would use the less specific undo for this:

from _pytest.monkeypatch import MonkeyPatch

class Test_Monkey_Patch_Env(unittest.TestCase):
    def setUp(self):
        self.monkeypatch = MonkeyPatch()

    def tearDown(self):
        self.monkeypatch.undo()

This reverts any changes made by using the MonkeyPatch instance, not just the specific setenv call shown above, and is therefore more secure.

Additionally there is the possibility to use MonkeyPatch as a context manager. This comes in handy if you want to use it just in one or a couple of tests. In this case you can write:

...
    def test_1(self):
        with MonkeyPatch() as mp:
            mp.setenv("TESTVAR1", "This env value is not persistent")
            do_something()       

The cleanup in this case is done on exiting the context manager.

like image 120
MrBean Bremen Avatar answered Nov 18 '25 21:11

MrBean Bremen


To remove env variable set by monkeypatch module, seems you have to call delenv method. You can call this delenv after you are done with setting & testing env variable using setenv method but I think tearDown would be the right place to put that delenv call.

def tearDown(self):
    print("tearDown")
    monkeypatch.delenv('TESTVAR1', raising=False)

I have not tested the code however should give you fair amount of idea whats needs to be done.

EDIT: Improved code to use setup * tearDown more efficiently. test_2 should raise OS Error since env variable has been deleted

import os
import unittest
import time
from _pytest.monkeypatch import MonkeyPatch

class Test_Monkey_Patch_Env(unittest.TestCase):
    def setUp(self):
        self.monkeypatch = MonkeyPatch()
        print("Setup")

    def test_1(self):
        self.monkeypatch.setenv("TESTVAR1", "This env value is persistent")

    def test_2(self):
        # I was expected the env TESTVAR1 set in test_1 using monkeypatch
        # should not persist across test cases. But it is.
        print(os.environ["TESTVAR1"])   # Should raise OS error

    def tearDown(self):
        print("tearDown")
        self.monkeypatch.delenv("TESTVAR1", raising=False)
            

if __name__ == '__main__':
    unittest.main()

Cheers!

like image 42
MaNKuR Avatar answered Nov 18 '25 19:11

MaNKuR



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!