2012-08-06 60 views
47

你如何模仿mock只读属性?如何用模拟模拟只读属性?

我想:

setattr(obj.__class__, 'property_to_be_mocked', mock.Mock()) 

但问题是,它则适用于类...打破我的测试中的所有实例。

你有什么想法吗?我不想嘲笑完整的对象,只有这个特定的属性。

回答

37

实际上,答案是(和往常一样)在documentation中,只是当我按照他们的例子将补丁应用于实例而不是类时。

这里是如何做到这一点:

class MyClass: 
    @property 
    def last_transaction(self): 
     # an expensive and complicated DB query here 
     pass 

在测试套件:

def test(): 
    # Make sure you patch on MyClass, not on a MyClass instance, otherwise 
    # you'll get an AttributeError, because mock is using settattr and 
    # last_transaction is a readonly property so there's no setter. 
    with mock.patch(MyClass, 'last_transaction') as mock_last_transaction: 
     mock_last_transaction.__get__ = mock.Mock(return_value=Transaction()) 
     myclass = MyClass() 
     print myclass.last_transaction 
+6

人应该用其他的例子。 'mock.PropertyMock'是实现它的方法! – vitiral 2016-09-06 19:21:02

73

我认为更好的办法是嘲笑财产PropertyMock,而不是嘲笑__get__方法直。

它在documentation中说明,搜索unittest.mock.PropertyMock: 一个模拟打算用作一个类的属性或其他描述符。 PropertyMock提供了__get____set__方法,因此您可以在提取时指定返回值。

方法如下:

class MyClass: 
    @property 
    def last_transaction(self): 
     # an expensive and complicated DB query here 
     pass 

def test(unittest.TestCase): 
    with mock.patch('MyClass.last_transaction', new_callable=PropertyMock) as mock_last_transaction: 
     mock_last_transaction.return_value = Transaction() 
     myclass = MyClass() 
     print myclass.last_transaction 
     mock_last_transaction.assert_called_once_with() 
+0

我不得不模拟一个以'@ property'装饰的类方法。当其他答案(以及其他许多问题的其他答案)没有答案时,这个答案对我有用。 – AlanSE 2016-05-19 20:32:04

+2

这是它应该完成的方式。我希望有一种方法来移动“接受”的答案 – vitiral 2016-09-06 19:20:38

+1

我发现包括上下文管理器调用中的返回值稍微更清晰: ''' with mock.patch('MyClass.last_transaction',new_callable = PropertyMock ,return_value = Transaction()): ... ''' – wodow 2017-06-30 17:38:02

3

大概的风格,但如果你喜欢装饰在测试中,@ jamescastlefield的answer的事情可以改变的东西是这样的:

class MyClass: 
    @property 
    def last_transaction(self): 
     # an expensive and complicated DB query here 
     pass 

class Test(unittest.TestCase): 
    @mock.patch('MyClass.last_transaction', new_callable=PropertyMock) 
    def test(mock_last_transaction): 
     mock_last_transaction.return_value = Transaction() 
     myclass = MyClass() 
     print myclass.last_transaction 
     mock_last_transaction.assert_called_once_with() 
0

如果你不您不想测试是否访问了嘲弄的财产,您只需使用预期的return_value即可对其进行修补。

with mock.patch(MyClass, 'last_transaction', Transaction()): 
    ...