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是否为不动产。(有房地产装饰师)。谢谢你的进一步解释。