模拟属性可在每次访问时更改

时间:2019-03-29 15:16:58

标签: 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 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”并且不包含USER。

我需要像conn.recbuf.side_effect = ['home',USER]之类的东西,但是仅在引用recbuf而不调用它时才可以工作。

3 个答案:

答案 0 :(得分:1)

如果您可以将conn.recbuf更改为property,则可以使用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)

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

答案 1 :(得分:0)

您要寻找的是side_effecthttps://docs.python.org/3/library/unittest.mock.html

假设mock是您的模拟对象。然后,您可以执行以下操作:

mock.side_effect = [a,b,c]

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

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

答案 2 :(得分:0)

我早些时候在解决类似的问题,并找到了使用PropertyMock的解决方案。就我而言,我想在测试用例中的其他位置存储一个值,以便在我要模拟的类上设置属性时通过side_effect调用方法来断言。

我认为这也应该适合您的情况(如果我了解您要执行的操作)。

解决方案是将PropertyMock用于属性recbuf,并将side_effect放在其中:

from unittest.mock import MagicMock, PropertyMock

conn = MagicMock()
type(conn).recbuf = PropertMock(side_effect=['home', USER])

只要访问属性recbuf,就会调用副作用,在这种情况下,每次都会遍历['home', USER]。我猜您也需要修补该USER值,以免出现名称错误。

希望这行得通/有帮助!