如何在python中修补常量

Posted

技术标签:

【中文标题】如何在python中修补常量【英文标题】:How to patch a constant in python 【发布时间】:2015-01-30 21:38:12 【问题描述】:

我的项目中有两个不同的模块。一个是配置文件,其中包含

LOGGING_ACTIVATED = False

这个常量用在第二个模块(我们称之为main)中,如下所示:

if LOGGING_ACTIVATED:
    amqp_connector = Connector()

在我的主模块测试类中,我想用值修补这个常量

True

不幸的是,以下不起作用

@patch("config.LOGGING_ACTIVATED", True)

这也不起作用:

@patch.object("config.LOGGING_ACTIVATED", True)

有人知道如何修补来自不同模块的常量吗?

【问题讨论】:

这对我有用...from mock import patch @patch("location.of.file.and.CONSTANT", mockValue) 【参考方案1】:

如果if LOGGING_ACTIVATED: 测试发生在模块级别,您需要先确保该模块尚未导入。模块级代码只运行一次(第一次在任何地方导入模块),您无法测试不会再次运行的代码。

如果测试在函数中,请注意使用的全局名称是LOGGING_ACTIVATED不是 config.LOGGING_ACTIVATED。因此,您需要在此处修补 main.LOGGING_ACTIVATED

@patch("main.LOGGING_ACTIVATED", True)

因为这是您要替换的实际参考。

另请参阅mock 文档中的Where to patch section。

您应该考虑将模块级代码重构为更可测试的东西。虽然您可以通过从 sys.modules 映射中删除模块对象来强制重新加载模块代码,但将您希望可测试的代码移动到函数中更为简洁。

如果你的代码现在看起来像这样:

if LOGGING_ACTIVATED:
    amqp_connector = Connector()

考虑改用函数:

def main():
    global amqp_connector
    if LOGGING_ACTIVATED:
        amqp_connector = Connector()

main()

或者生成一个带有属性的对象。

【讨论】:

打败我吧,我正忙着弄清楚patch() 是否能够修补__main__ 模块,以防万一这就是“称之为主”的意思。 @SteveJessop:记录在案:是的。就 Python 而言,__main__ 只是另一个模块,所以patch('__main__.somename', somevalue) 可以工作。 感谢您的快速答复。 if 语句确实在模块级别。在我的测试类中,我导入了模块。那么单测试方法就没有机会重写了吗? @d.a.d.a:您可以通过从sys.modules 中删除模块来强制重新加载模块。 if 'main' in sys.modules: del sys.modules['main']. @d.a.d.a:但是,我会重构模块以使用函数,通过一次调用从顶层运行。这样您就可以改为测试 函数【参考方案2】:

你面临的问题是你在定义它的地方模拟,你应该修补它在哪里使用。

模拟一个项目的使用位置,而不是它的来源。

我给你留下一些示例代码,这样你就可以理解这个想法。

project1/constants.py

INPUT_DIRECTORY="/input_folder"

project1/module1.py

from project1.constants import INPUT_DIRECTORY
import os

def clean_directories():
    for filename in os.listdir(INPUT_DIRECTORY):
        filepath = os.path.join(directory, filename)
        os.remove(filepath)

project1/tests/test_module1.py

import mock, pytest

def test_clean_directories(tmpdir_factory):
    """Test that folders supposed to be emptied, are effectively emptied"""

    # Mock folder and one file in it
    in_folder = tmpdir_factory.mktemp("in")
    in_file = in_folder.join("name2.json")
    in_file.write("'asd': 3")

    # Check there is one file in the folder
    assert len([name for name in os.listdir(in_folder.strpath) if os.path.isfile(os.path.join(path, name))]) == 1

    # As this folder is not a parameter of the function, mock it.
    with mock.patch('project1.module1.INPUT_DIRECTORY', in_folder.strpath):
        clean_directories()

    # Check there is no file in the folder
    assert len([name for name in os.listdir(in_folder.strpath) if os.path.isfile(os.path.join(path, name))]) == 0

所以重要的一行是这一行:

with mock.patch('project1.module1.INPUT_DIRECTORY', in_folder.strpath):

看,值是在 在哪里使用被模拟的,而不是在 constants.py 中(在哪里定义

【讨论】:

天哪,关于“在哪里使用”的评论为我节省了很多时间。谢谢! 你嘲笑的一切都是如此@Dr_Zaszuś =) .. 当我了解到测试口头禅时,那一刻我知道如何测试【参考方案3】:

在遇到类似问题时发现了这个帖子,什么对我有用:

from unittest import mock

@mock.patch('<name_of_module>.<name_of_file>.<CONSTANT_NAME>', <value_to_replace_with>)
class Test_<name_of_test_suit>(unittest.TestCase):

  def test_<name_of_test>(self):
    <test_body>

请记住,您需要和__init__.py 将包含文件的目录视为包。 https://docs.python.org/3/tutorial/modules.html#:~:text=The%20__init__.py,on%20the%20module%20search%20path.

附: 看看https://chase-seibert.github.io/blog/2015/06/25/python-mocking-cookbook.html

【讨论】:

以上是关于如何在python中修补常量的主要内容,如果未能解决你的问题,请参考以下文章

如何修补由 Python 数据形状中的装饰器注册的方法?

如何在unmocked类中使用autospec修补classmethod?

如何使用装饰器修补sys属性?

猴子在Python的另一个模块中修补一个类

如何在 Python 中记录模块常量?

我可以在包装函数之前修补 Python 装饰器吗?