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.
2 Answers 2
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"
Comments
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.
Comments
Explore related questions
See similar questions with these tags.