11

Consider the following:

class MockResponse:
 status_code = 200
 @staticmethod
 def json():
 return {'key': 'value'}
 # where api_session is a fixture
def test_api_session_get(monkeypatch, api_session) -> None:
 def mock_get(*args, **kwargs):
 return MockResponse()
 monkeypatch.setattr(requests.Session, 'get', mock_get)
 response = api_session.get('endpoint/') # My wrapper around requests.Session
 assert response.status_code == 200
 assert response.json() == {'key': 'value'}
 monkeypatch.assert_called_with(
 'endpoint/',
 headers={
 'user-agent': 'blah',
 },
 )

How can I assert that the get I am patching gets called with '/endpoint' and headers? When I run the test now I get the following failure message:

FAILED test/utility/test_api_session.py::test_api_session_get - AttributeError: 'MonkeyPatch' object has no attribute 'assert_called_with'

What am I doing wrong here? Thanks to all those of who reply in advance.

asked Dec 15, 2020 at 14:55

2 Answers 2

19

Going to add another response that uses monkeypatch rather than "you can't use monkeypatch"

Since python has closures, here is my poor man's way of doing such things with monkeypatch:

patch_called = False
def _fake_delete(keyname):
 nonlocal patch_called
 patch_called = True
 assert ...
monkeypatch.setattr("mymodule._delete", _fake_delete)
res = client.delete(f"/.../{delmeid}"). # this is a flask client
assert res.status_code == 200
assert patch_called

In your case, since we are doing similar things with patching an HTTP servers method handler, you could do something like (not saying this is pretty):

param_called = None
def _fake_delete(param):
 nonlocal param_called
 patch_called = param
 assert ...
monkeypatch.setattr("mymodule._delete", _fake_delete)
res = client.delete(f"/.../{delmeid}")
assert res.status_code == 200
assert param_called == "whatever this should be"
answered Apr 13, 2021 at 11:45
Sign up to request clarification or add additional context in comments.

Comments

12

You need a Mock object to call assert_called_with - monkeypatch does not provide that out of the box. You can use unittest.mock.patch with side_effect instead to achieve this:

from unittest import mock
import requests
...
@mock.patch('requests.Session.get')
def test_api_session_get(mocked, api_session) -> None:
 def mock_get(*args, **kwargs):
 return MockResponse()
 mocked.side_effect = mock_get
 response = api_session.get('endpoint/') 
 ...
 mocked.assert_called_with(
 'endpoint/',
 headers={
 'user-agent': 'blah',
 },
 )

Using side_effect is needed to still get a mock object (mocked in this case, of type MagickMock), instead of just setting your own object in patch, otherwise you won't be able to use the assert_called_... methods.

To clarify: when I write "monkeypatch does not provide that out of the box" this is related to the usage of the assert_called... methods. It does not imply that you can't use monkeypatch to implement a similar functionality, as is nicely shown in the answer by Tommy.

answered Dec 15, 2020 at 19:10

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.