我想模拟用于初始化类级(非实例)属性的模块级函数。这是一个简单的例子:
# a.py
def fn():
return 'asdf'
class C:
cls_var = fn()
这是一个试图模仿a.fn()的单元测试:
# test_a.py
import unittest, mock
import a
class TestStuff(unittest.TestCase):
# we want to mock a.fn so that the class variable
# C.cls_var gets assigned the output of our mock
@mock.patch('a.fn', return_value='1234')
def test_mock_fn(self, mocked_fn):
print mocked_fn(), " -- as expected, prints '1234'"
self.assertEqual('1234', a.C.cls_var) # fails! C.cls_var is 'asdf'
我认为问题是where to patch,但我已尝试导入两种变体而没有运气。我甚至尝试将import语句移动到test_mock_fn()中,以便模拟的a.fn()将存在"存在"在a.C进入范围之前 - 不,仍然失败。
非常感谢任何见解!
答案 0 :(得分:3)
这里实际发生的是,当您实际导入模块时,fn()
已经执行了。因此,在您已经评估了存储在类属性中的方法之后,模拟就会出现。
因此,当您尝试模拟方法时,您尝试进行的测试为时已晚。
如果您只是在方法中添加print语句,您甚至可以看到这种情况:
def fn():
print("I have run")
return "asdf"
在您的测试模块中,当您导入a
并且只是在没有运行测试的情况下运行时,您会看到I have run
将出现在您的控制台输出中,而不会从{{1}明确地运行任何内容}模块。
所以,你可以采取两种方法。您可以使用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)
,但是通过这种嘲弄,您现在正在持有' 1234' fn
中已设置cls_var
的{{1}}。
以下建议(可能不太理想,因为它需要进行设计更改)将需要修改您使用类属性的原因。因为如果您实际上将该类属性设置为实例属性,那么当您创建PropertyMock
实例时,您的方法将执行,此时它将使用您的模拟。
所以,你的班级看起来像:
C
你的测试看起来像:
class C:
def __init__(self):
self.var = fn()
答案 1 :(得分:0)
即使@ idjaw的回答是正确的,也要正确解释发生了什么我认为最简洁,简单和直接的方法是直接用值修改a.C.cls_var
属性而不是模拟。
@mock.patch('a.C.cls_var', '1234')
这足以满足您的一切需求:当您必须替换应该像属性一样的属性时,使用PropertyMock
非常有用。
关于为什么你的方法没有工作的每一个细节都要看看@ idjaw的答案。