每次访问时都会更改的模拟属性



这是我想要测试的函数:

def send_something():
    conn = lib.conn.SSH1
    conn.open()
    conn.send('lsn')
    if 'home' not in conn.recbuf:
        return lib.FAIL
    conn.send('whoamin')
    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('lsn')
    mock_conn.SSH1.send.assert_any_call('whoamin')

显然,这失败了,因为conn.recbuf只是"家",不包含USER。

我需要类似 conn.recbuf.side_effect = ['home', USER] 的东西,但在只引用 recbuf 而不调用它时会起作用。

如果您能够将 conn.recbuf 更改为属性,则可以使用 PropertyMock 来实现所需的效果。

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)

但是,对于您的实际问题,我认为对您的问题的评论似乎包括合理的方法。

您要查找的是side_effect:https://docs.python.org/3/library/unittest.mock.html

假设mock是您的模拟对象。然后你可以这样做:

mock.side_effect = [a,b,c]

然后每次调用 mock 时,都会返回列表中的下一个值。

mock() # -> a
mock() # -> b
mock() # -> c

我之前曾遇到过类似的问题,并使用PropertyMock找到了解决方案。在我的例子中,我想在测试用例中的其他地方存储一个值,以便在我模拟的类上设置属性时通过side_effect调用方法来断言。

我认为它也应该适用于您的情况(如果我了解您要做什么(。

解决方案是为您的财产recbuf使用PropertyMock,并将side_effect放在那里:

from unittest.mock import MagicMock, PropertyMock
conn = MagicMock()
type(conn).recbuf = PropertyMock(side_effect=['home', USER])

每当访问属性recbuf时,都会调用副作用,在这种情况下,每次都会循环访问['home', USER]。我想您还需要修补该USER值,以免出现名称错误。

希望这有效/有帮助!

最新更新