Reputation: 7937
I'm learning how to write tests with py.test, but I can't get idea how to mock external calls.
Let say I have a code for testing:
app.py:
import random
def trade_robot(stock, price):
return ' '.join((random.choice(('buy', 'sell', 'keep')), stock))
And I want to be sure I check case for random.choice deciding I need to buy (and I want to check it properly join strings for 'buy' case).
my test code should look like this:
import pytest
from app import trade_robot
import sys
def test_buy_case():
# some mock magic here
assert trade_robot('AAPL', 500) == 'buy AAPL'
if __name__ == "__main__":
pytest.main("-v %s" % sys.argv[0])
What should be in 'some mock magic' to make this test passing every time? Thanks!
Upvotes: 1
Views: 4226
Reputation: 34312
That can be done with the library mock
, which is included into standard library since Python 3.3.
First, with a context manager:
import random
with mock.patch.object(random, 'choice') as m:
m.return_value = 'buy'
print random.choice(['anything']) # prints 'buy' regardless of the arguments
The same with a decorator:
@mock.patch.object(random, 'choice')
def test_buy(m):
m.return_value = 'buy'
print random.choice(['anything'])
The library also allows making assertions regarding mock calls and irreplaceable for unit testing. Alternatively, some people prefer explicit dependency inversion, which means passing the function random.choice
into your code as a function/method/constructor argument and replacing it with a mock in a test.
Upvotes: 6
Reputation: 15
You could use the 'patch' decorator to mock external calls:
http://www.voidspace.org.uk/python/mock/patch.html
E.g. you could mock the 'random.choice' with
@patch('random.choice')
def test_foo(choice):
choice.return_value = some value
The key is where to patch: http://www.voidspace.org.uk/python/mock/patch.html#id1
Upvotes: 1