Mock
Tip
If you have any issues with patch not working as expected, check
the notes ref using mock (without py.test) below…
The unittest.mock library is included with python 3.
To use the mock module with py.test, install the pytest-mock module:
pip install pytest-mock
Examples (using py.test)
I like this syntax:
from unittest.mock import patch, Mock, mock_open
def mock_ip():
return '12.13.14.15'
@patch('customer.Customer.create', return_value='153356')
@patch('service.ip_address_from_url', return_value=mock_ip())
@patch.multiple('customer.Customer',
id_for_email=Mock(return_value=46419),
get_customer=Mock(side_effect=[{}, mock_simple]),
)
def test_register(self, patch_ip, patch_create):
Tip
The patch variables e.g. patch_ip are in reverse order i.e.
The first @patch decorator is passed to the function in the
second position.
@pytest.mark.django_db
def test_post_card_refresh(client, mocker):
mocker.patch('stripe.Customer.create')
Exception
from unittest import mock
@pytest.mark.django_db
def test_process_payments_fail(mocker):
with mock.patch('stripe.Customer.create') as mock_customer:
mock_customer.side_effect = CheckoutError('Mock')
Property
To mock a property e.g:
class SalesLedger(models.Model):
@property
def checkout_can_charge(self):
return True
Use the PropertyMock e.g:
with mock.patch(
'example_checkout.models.SalesLedger.checkout_can_charge',
new_callable=mock.PropertyMock,
) as mock_checkout_can_charge:
# mock return
mock_checkout_can_charge.return_value = False
Issues
import
I tried to mock a function like this:
def search(index_name, criteria, page_number=None):
But it didn’t seem to work, so I made a private function e.g. _search and
mocked that instead:
def search(index_name, criteria, page_number=None):
# call the private function
return _search(index_name, criteria, page_number)
# the mock
with mock.patch('search.service._search') as m:
Note
I don’t understand why this works, but it does!
with
I was using with mock and put the code I was testing after the with
block… so there was no mocking by the time my code was running!
mock (without py.test)
method
Using mock with py.test appears to stop mock working as documented.
If you have any issues, try ignoring the mocker fixture:
from unittest import mock
with mock.patch('stripe.Customer.retrieve') as mock_retrieve:
mock_retrieve.return_value = {
'default_card': '1234',
'cards': {
'data': [
{
'id': '1234',
'exp_month': '8',
'exp_year': '1986',
},
],
},
}
Thank you Helen Sherwood-Taylor for your excellent talk on Managing mocks
object
I was testing some code in another module. The import and code in the other module looked like this:
from django.core import mail
msg = mail.EmailMultiAlternatives(
# ... do some work...
for resp in msg.mandrill_response:
Here is the test code:
from unittest import mock
with mock.patch('django.core.mail.EmailMultiAlternatives') as mock_mail:
mock_mail.return_value.mandrill_response = [{
"email": "abc@test.com",
"status": "rejected",
"_id": "123",
"reject_reason": "hard-bounce"
}]
Note
I was surprised to see that setting up the return value for the
mandrill_response was written with the return_value before
the method name.
Note
You have to patch the correct import path (see Where to patch).
I spent hours trying to find where to patch, but the issue was a
completely different one (I hadn’t got my test code inside the
with block).