如何在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中修补常量的主要内容,如果未能解决你的问题,请参考以下文章