Modifying global variables in Python unittest framework

Question:

I am working on a series of unit tests in Python, some of which depend on the value of a configuration variable. These variables are stored in a global Python config file and are used in other modules. I would like to write unit tests for different values of the configuration variables but have not yet found a way to do this.

I do not have the possibility to rewrite the signatures of the methods I’m testing.

This is what I would like to achieve:

from my_module import my_function_with_global_var

class TestSomething(self.unittest):

    def test_first_case(self):
         from config import MY_CONFIG_VARIABLE
         MY_CONFIG_VARIABLE = True
         self.assertEqual(my_function_with_global_var(), "First result")

    def test_second_case(self):
         from config import MY_CONFIG_VARIABLE
         MY_CONFIG_VARIABLE = False
         self.assertEqual(my_function_with_global_var(), "Second result")

Thanks.

Edit: Made the example code more explicite.

Asked By: badzil

||

Answers:

You code imports MY_CONFIG_VARIABLE into the local scope and then immediately overwrites the name with a different object. That won’t change the value in the config module. Try

import config
config.MY_CONFIG_VARIABLE = False

instead.

Answered By: Sven Marnach

Use unittest.mock.patch as in @Flimm’s answer, if that’s available to you.


Original Answer

Don’t do this:

from my_module import my_function_with_global_var

But this:

import my_module

And then you can inject MY_CONFIG_VARIABLE into the imported my_module, without changing the system under test like so:

class TestSomething(unittest.TestCase): # Fixed that for you!

    def test_first_case(self):
         my_module.MY_CONFIG_VARIABLE = True
         self.assertEqual(my_module.my_function_with_global_var(), "First result")

    def test_second_case(self):
         my_module.MY_CONFIG_VARIABLE = False
         self.assertEqual(my_module.my_function_with_global_var(), "Second result")

I did something similar in my answer to How can I simulate input to stdin for pyunit? .

Answered By: johnsyweb

You probably want to mock those global variables instead. The advantage of this is that the globals get reset once you’re done. Python ships with a mocking module that lets you do this.

unittest.mock.patch be used as a decorator:

class TestSomething(self.unittest):

    @patch('config.MY_CONFIG_VARIABLE', True)
    def test_first_case(self):
         self.assertEqual(my_function_with_global_var(), "First result")

You can also use it as a context manager:

    def test_first_case(self):
        with patch('config.MY_CONFIG_VARIABLE', True):
            self.assertEqual(my_function_with_global_var(), "First result")
Answered By: Flimm