Python 模拟属性,以便在每次访问时进行更改
下面是我要测试的函数:Python 模拟属性,以便在每次访问时进行更改,python,unit-testing,mocking,Python,Unit Testing,Mocking,下面是我要测试的函数: def send_something(): conn = lib.conn.SSH1 conn.open() conn.send('ls\n') if 'home' not in conn.recbuf: return lib.FAIL conn.send('whoami\n') if USER not in conn.recbuf: return lib.FAIL return l
def send_something():
conn = lib.conn.SSH1
conn.open()
conn.send('ls\n')
if 'home' not in conn.recbuf:
return lib.FAIL
conn.send('whoami\n')
if USER not in conn.recbuf:
return lib.FAIL
return lib.PASS
每次调用conn.send()时,调用的输出都存储在conn.recbuf中。为了测试这一点,我需要conn.recbuf在每次调用时都不同。(我知道我可以将conn.recbuf更改为一个同时包含home和USER的字符串,但这对更复杂的函数不起作用)
以下是我在测试中得出的结论:
@mock.patch.object(demo_sequence.lib, 'conn')
def test_send_something(mock_conn):
mock_conn.SSH1.recbuf = 'home'
assert demo_sequence.lib.PASS == demo_sequence.send_something()
mock_conn.SSH1.send.assert_any_call('ls\n')
mock_conn.SSH1.send.assert_any_call('whoami\n')
显然,这会失败,因为conn.recbuf只是“home”,不包含用户
我需要像conn.recbuf.side_effect=['home',USER]这样的东西,但只要引用recbuf而不调用它,它就会起作用。您要找的是
side_effect
:
假设mock
是您的模拟对象。然后你可以这样做:
mock.side_effect = [a,b,c]
然后,每次调用mock
,都会返回列表中的下一个值
mock() # -> a
mock() # -> b
mock() # -> c
如果您能够将conn.recbuf更改为a,则可以使用a来实现所需的效果
from unittest import mock
class Dummy:
def __init__(self, myattribute):
self._myattribute = myattribute
@property
def myattribute(self):
return self._myattribute
def test():
with mock.patch('__main__.Dummy.myattribute', new_callable=mock.PropertyMock) as mocked_attribute:
mocked_attribute.side_effect = [4,5,6]
d = Dummy("foo")
print(d.myattribute)
print(d.myattribute)
print(d.myattribute)
然而,对于您的实际问题,我认为对您的问题的评论似乎包含了合理的方法。我之前曾与类似的问题争论过,并使用
PropertyMock
找到了解决方案。在我的例子中,我想在测试用例的其他地方存储一个值,以便在我模拟的类上设置属性时,通过副作用调用一个方法来进行断言
我认为这也适用于你的情况(如果我理解你的意图的话)
解决方案是对您的属性使用PropertyMock
,并将副作用放入其中:
from unittest.mock import MagicMock, PropertyMock
conn = MagicMock()
type(conn).recbuf = PropertyMock(side_effect=['home', USER])
每当访问属性recbuf
时,就会调用副作用,在这种情况下,每次都会遍历['home',USER]
。我想您也需要修补USER
值,这样就不会出现名称错误
希望这有用 您是否只想模拟发送
,以便它除了更新recbuff
之外不做任何事情?如果不知道“conn”实际上是什么类型,就很难提供一个好的解决方案。conn库很奇怪(糟糕)。SSH1也必须被嘲笑,因为它并不总是存在。这就是为什么我选择mock作为lib.conn级别。我可能还建议不要在send\u something
中硬编码conn
的值,而是将其设置为函数参数。然后,您可以将任何想要的对象作为参数传递,而不必使用mock.patch
。如果您正在进行单元测试,那么最好完全模拟conn。如果您希望看到lib.conn库满足合同要求,还可以对其进行另一种特殊的测试。此外,如果您的代码在许多地方使用了“坏”库,也可以在“坏”库周围制作一个垫片/适配器。否决,因为这正是他在最后一句话中提到的,并解释了它不起作用的原因:他需要的正是属性的这种行为,而不是可调用的。明白了。通过模拟\uuuu getattr\uuuuu
方法,您可以实现属性的相同功能,但这可能有点复杂。您能否详细说明模拟getattr以实现我的问题的解决方案?当您执行a.x时,它会被转换为..\uu getattr\uuuux('x')。所以你可以试着模仿一下。这种方法确实满足了我的需要。我已经看到一些评论和以前的回答说使用PropertyMock,但我从未检查recbuf是否为不动产。(有房地产装饰师)。谢谢你的进一步解释。