Python:如何模拟类属性初始值设定项函数
我想模拟一个用于初始化类级(不是实例)属性的模块级函数。下面是一个简化的示例:Python:如何模拟类属性初始值设定项函数,python,static-members,python-mock,class-attributes,Python,Static Members,Python Mock,Class Attributes,我想模拟一个用于初始化类级(不是实例)属性的模块级函数。下面是一个简化的示例: # a.py def fn(): return 'asdf' class C: cls_var = fn() 下面是一个试图模拟a.fn()的单元测试: 我相信问题是,但我尝试了进口的两种变体,但都没有成功。我甚至尝试将import语句移动到test_mock_fn()中,以便在a.C进入作用域之前模拟的a.fn()将“存在”——不,仍然失败 如有任何见解,将不胜感激 这里实际发生的是,
# a.py
def fn():
return 'asdf'
class C:
cls_var = fn()
下面是一个试图模拟a.fn()的单元测试:
我相信问题是,但我尝试了进口的两种变体,但都没有成功。我甚至尝试将import语句移动到test_mock_fn()中,以便在a.C进入作用域之前模拟的a.fn()将“存在”——不,仍然失败
如有任何见解,将不胜感激 这里实际发生的是,当您实际导入模块时,
fn()
应该已经执行了。因此,在您已经计算了存储在class属性中的方法之后,mock就会出现
因此,当您尝试模拟该方法时,您尝试进行的测试已经太迟了
如果您只需在方法中添加一个print语句,您甚至可以看到这种情况:
def fn():
print("I have run")
return "asdf"
在您的测试模块中,当您导入a
并在不运行测试的情况下运行时,您将看到I have run
将出现在控制台输出中,而不从a
模块显式运行任何内容
所以,这里有两种方法。您可以使用PropertyMock
模拟类属性,使其符合您希望它存储的内容,如下所示:
@mock.patch('a.C.cls_var', new_callable=PropertyMock)
def test_mock_fn(self, mocked_p):
mocked_p.return_value = '1234'
self.assertEqual('1234', a.C.cls_var)
现在,您还必须意识到,通过这样做,您实际上仍然在运行fn
,但通过此模拟,您现在在cls_var
中使用您设置的PropertyMock
持有“1234”
下面的建议(可能不太理想,因为它需要更改设计)需要修改使用class属性的原因。因为如果您实际将该类属性设置为实例属性,那么当您创建C
的实例时,您的方法将执行,此时它将使用您的模拟
因此,您的类看起来像:
class C:
def __init__(self):
self.var = fn()
您的测试将如下所示:
@mock.patch('a.fn', return_value='1234')
def test_mock_fn(self, mocked_p):
self.assertEqual('1234', a.C().var)
即使@idjaw的答案是正确的,并且正确地解释了发生了什么,我认为最简洁、简单和直接的方法是通过一个值直接修补
a.C.cls_var
属性,而不是模拟
@mock.patch('a.C.cls_var', '1234')
这就足够满足您的所有需要:仅当您必须替换行为类似于属性的属性时,使用PropertyMock
就很有用
有关为什么您的方法不起作用的每一个细节,请查看@idjaw的答案。您是否尝试将导入更改为使用from语句?从一个进口的fnHi Ranier-yep,试过了;不走运。(当我提到“…导入的两种变体…”时,我应该更清楚。mock上的Python文档给出了使用
import a
和从import SomeClass
导入的示例。我尝试了两种口味)Hi-idjaw-嘿,我喜欢PropertyMock
的想法!我试试看。让fn()
运行并不理想,但值得一试。关于你的第二个想法,相信我,我很想重构这段代码。当然,我给出的例子是对真正问题的抽象,它涉及到SQLAlchemy。SQLAlchemy广泛使用了类属性,我几乎无法对其进行更改。谢谢
@mock.patch('a.C.cls_var', '1234')