Python:如何模拟类属性初始化函数

时间:2016-02-21 01:17:20

标签: python static-members python-mock class-attributes

我想模拟用于初始化类级(非实例)属性的模块级函数。这是一个简单的例子:

# 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进入范围之前 - 不,仍然失败。

非常感谢任何见解!

2 个答案:

答案 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的答案。