【问题标题】:Modifying global variables in Python unittest framework在 Python unittest 框架中修改全局变量
【发布时间】:2011-06-07 16:10:18
【问题描述】:

我正在使用 Python 进行一系列单元测试,其中一些取决于配置变量的值。这些变量存储在全局 Python 配置文件中,并在其他模块中使用。我想为配置变量的不同值编写单元测试,但还没有找到方法。

我无法重写我正在测试的方法的签名。

这就是我想要实现的目标:

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")

谢谢。

编辑:使示例代码更加明确。

【问题讨论】:

    标签: python unit-testing global-variables


    【解决方案1】:

    您可能希望改为模拟这些全局变量。这样做的好处是,一旦你完成,全局变量就会被重置。 Python 附带了一个模拟模块,可让您执行此操作。

    unittest.mock.patch 用作装饰器:

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

    您也可以将其用作上下文管理器:

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

    【讨论】:

    • 绝妙的方法:简单有效。感谢分享。对@Michele 的编辑特别有用。
    • 就我而言,我并不关心初始值,但需要验证全局是否已写入。这可以使用with patch('config') as mock_configself.assertEqual(True, mock_config.MY_CONFIG_VARIABLE)
    • 是否可以按照类似的方法在实例方法中修补局部变量?
    • 当全局变量用作函数的默认参数值时,这似乎不起作用,例如def my_function_with_global_var(s=MY_CONFIG_VARIABLE).
    【解决方案2】:

    使用unittest.mock.patch,如@Flimm's answer,如果您可以使用的话。


    原答案

    不要这样做:

    from my_module import my_function_with_global_var
    

    但是这个:

    import my_module
    

    然后您可以将MY_CONFIG_VARIABLE 注入到导入的my_module 中,而无需像这样更改被测系统:

    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")
    

    我在my answer toHow can I simulate input to stdin for pyunit?做了类似的事情。

    【讨论】:

    • @badzil:太好了。现在您已经有了单元测试覆盖率,您可以删除(需要)全局变量 ;-)
    • 但是在 python 中使用全局变量来定义常量有什么缺点吗?我读到如果你把它们放在你导入的模块的__init__ 中就可以了。
    • 这种方法会给您带来一个微妙的错误,因为测试完成时my_module.MY_CONFIG_VARIABLE 的值不会重置。如果后续测试在同一进程中运行,使用 MY_CONFIG_VARIABLE 而不期望它被更改,您的测试可能会失败或通过,具体取决于它们执行的顺序。
    • 你是对的。这是微妙但主要的缺点,不是我在 2017 年做事的方式。当我写这个答案时,我不确定 mock.patch 甚至在 2011 年与 Python 一起发布。
    • @Johnsyweb 更新答案怎么样?自从我在 2017 年来到这里后,我会将我的反对票改为赞成票 :)
    【解决方案3】:

    您将MY_CONFIG_VARIABLE 代码导入本地范围,然后立即用不同的对象覆盖名称。这不会改变 config 模块中的值。试试

    import config
    config.MY_CONFIG_VARIABLE = False
    

    改为。

    【讨论】:

    • 也许我忘了提点别的。我正在测试的函数在一个单独的模块中,因此已经导入了我需要修改的全局变量。您提出的修改只修改了测试文件中的全局变量。
    • @badzil:最好的选择可能是不要在您正在测试的模块中使用from config import MY_CONFIG_VARIABLE,而是使用import config 并以config.MY_CONFIG_VARIABLE 的身份访问变量。
    • 谢谢。尽管我希望不修改我正在测试的模块,但这仍然有效。我猜测由于要测试的模块使用 import ... from ... 指令,因此无法在导入后更改全局变量的值。
    • @badzil:如果该值是不可变的(如示例中的 bool),则您无法更改它。这就是不可变对象的本质。 -- 另一种选择是reload() 并在更改config 中的值后重新导入所有模块。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多