Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python mocking, return different value every call

I am testing a method, geodata_collect._request_loc_data(), and within that method I call another method, geodata_collect.utils.loadJSON(...) which I need to mock in order to unit-test the first mentioned method.

My problem is that I need geodata_collect.utils.loadJSON(...) to return a different value the third time it is called from within geodata_collect._request_loc_data().

I have been exploring MagicMock and side_effect in order to do this.

mock = MagicMock()
mock.side_effect = [self._create_request_dict(next_page_token=True),
    self._create_request_dict(next_page_token=True), self._create_request_dict()]

with patch('geodata_collect.utils.loadJSON',return_value=mock):
    geodata_collect._request_loc_data()

However, when geodata_collect.utils.loadJSON(...) is called from within geodata_collect._request_loc_data() the MagicMock class is returned, instead of the actual value.

<MagicMock id='140642209064888'>

What should be returned:

{'status': 'OK', 'next_page_token': 'Next Page EXISTS!!', 'result': [1, 2, 3, 4, 5]}
{'status': 'OK', 'next_page_token': 'Next Page EXISTS!!', 'result': [1, 2, 3, 4, 5]}
{'status': 'OK', 'result': [1, 2, 3, 4, 5]}
like image 570
Carl Rynegardh Avatar asked Feb 20 '18 19:02

Carl Rynegardh


1 Answers

You have set the return value of the call to a mock object. So that’s what will be returned! You set return values (side effects) for calls to that call result, so geodata_collect.utils.loadJSON()().

Set the side_effect argument in the patch() call:

results = [
    self._create_request_dict(next_page_token=True),
    self._create_request_dict(next_page_token=True),
    self._create_request_dict()]

with patch('geodata_collect.utils.loadJSON', side_effect=results):
    geodata_collect._request_loc_data()

Or the side_effect attribute on the mock object returned when entering the context manager:

with patch('geodata_collect.utils.loadJSON', side_effect=results) as load_mock:
    load_mock.side_effect = [
        self._create_request_dict(next_page_token=True),
        self._create_request_dict(next_page_token=True),
        self._create_request_dict()]
    geodata_collect._request_loc_data()

Capturing the mock object the patch() context manager creates with as <name> is generally a good idea anyway as you now have access to it to assert if it was called.

You can also pass in the MagicMock instance you created up front, just pass it in as the second argument, or use the name new:

mock = MagicMock()
mock.side_effect = [self._create_request_dict(next_page_token=True),
    self._create_request_dict(next_page_token=True), self._create_request_dict()]

with patch('geodata_collect.utils.loadJSON', mock):  # or new=mock
    geodata_collect._request_loc_data()

patch() will then replace geodata_collect.utils.loadJSON with that instance, and calling it will use the side_effect list set.

like image 138
Martijn Pieters Avatar answered Nov 10 '22 21:11

Martijn Pieters