Reputation: 159
Here is the function I want to test:
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
Every time I call conn.send() the output of the call is stored in conn.recbuf. To test this I need conn.recbuf to be different each time it is called. (I know I could just change conn.recbuf to be a string that contains both home and USER but that won't work for more complicated functions)
Here is what I have come up with so far in my test:
@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')
Obviously this fails because conn.recbuf is only 'home' and does not contain USER.
I need something like conn.recbuf.side_effect = ['home', USER] but will work when just referencing recbuf and not calling it.
Upvotes: 3
Views: 2588
Reputation: 293
I was wrangling with a similar issue earlier and found a solution using the PropertyMock
. In my case I wanted to store a value elsewhere in the test case to assert against by calling a method via a side_effect
when a property was set on the class I was mocking.
I think it should work for your case as well (if I've understood what you're trying to do).
The solution is to use a PropertyMock
for your property recbuf
and put the side_effect
in there:
from unittest.mock import MagicMock, PropertyMock
conn = MagicMock()
type(conn).recbuf = PropertyMock(side_effect=['home', USER])
The side effect will be called whenever the property recbuf
is accessed, in this case iterating through ['home', USER]
each time. I guess you'll need to patch that USER
value too so you don't get a name error.
Hope this works / helps!
Upvotes: 0
Reputation: 1169
If you are able to change conn.recbuf to be a property, you could use a PropertyMock to achieve the desired effect.
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)
However for your actual problem I think the comments to your question seem to include reasonable approaches.
Upvotes: 2
Reputation: 21285
What you're looking for is side_effect
: https://docs.python.org/3/library/unittest.mock.html
Let's say mock
is your mock object. Then you can do this:
mock.side_effect = [a,b,c]
then each time you call mock
, the next value from the list is returned.
mock() # -> a
mock() # -> b
mock() # -> c
Upvotes: 0