2

I have used flask_restful to create one of my API. The resource or API class called TotalUserResponse has a method called process_get_request that I am trying to unittest.

def process_get_request(self):
    params = parser_get.parse_args()
    user_id = params.get('user_id')
    if not user_id:
        raise ValueError("User id is empty")

    user = session.query(User).get(user_id)
    if not user:
        raise MyValidationError("User not found")

    # total applied, favourited and archived
    aggregated_actions = self.get_aggregated_actions(user_id)
    response = dict(applied=aggregated_actions[0],
                    favourited=aggregated_actions[1],
                    archived=aggregated_actions[2])

    return response

unittest -

@mock.patch('application.resources.user_response.TotalUserResponse', autospec=True)
@mock.patch('application.models.session.query', autospec=True)
@mock.patch('flask_restful.reqparse.RequestParser.parse_args', autospec=True)
def test_process_get_request(self, parse_args_mock, query_mock, total_user_response_mock):

    parse_args_mock.return_value = dict(user_id='xxxxxx')
    query_mock.return_value.get.return_value = 'something non empty'
    expected_applied, expected_favourited, expected_archived = 5, 10, 20
    total_user_response_mock.return_value.get_aggregated_actions.return_value = (expected_applied, expected_favourited, expected_archived)

    expected_response = dict(applied=expected_applied,
                             favourited=expected_favourited, archived=expected_archived)

    self.assertEqual(self.total_user_response.process_get_request(), expected_response)

My unittest fails saying -

AssertionError: {'applied': <MagicMock name='query().filter().first().__getitem__()' id='1543892 [truncated]... != {'applied': 5, 'archived': 20, 'favourited': 10}

From the above error message, I understand that get_aggregated_user_actions is not getting mocked. When I debug it, I see that the debugger takes me inside the function also which wouldn't have happened if it was properly mocked.

What's wrong? Please help me out.

Hussain
  • 5,057
  • 6
  • 45
  • 71

1 Answers1

1

In your test you use self.total_user_response.process_get_request(): you call process_get_request() it is the method of self.total_user_response and not the you mocked before. IMHO it is ever better and simple to patch the static references instead of the object references. Patching object make test more complicated and harder to understand/maintain, use it just as last resource.

To reduce this kind of problems is better rewrite your test like:

@mock.patch('application.resources.user_response.TotalUserResponse.get_aggregated_actions', autospec=True)
@mock.patch('application.models.session.query.get', return_value='something non empty', autospec=True)
@mock.patch('flask_restful.reqparse.RequestParser.parse_args', autospec=True, return_value=dict(user_id='xxxxxx'))
def test_process_get_request(self, parse_args_mock, get_mock, get_aggregated_mock):
    expected_applied, expected_favourited, expected_archived = 5, 10, 20
    get_aggregated_mock.return_value = (expected_applied, expected_favourited, expected_archived)

    expected_response = dict(applied=expected_applied,
                             favourited=expected_favourited, archived=expected_archived)

    self.assertEqual(self.total_user_response.process_get_request(), expected_response)

Maybe it contains some error, but I hope the idea is clear:

  • Mock just what you exactly need to mock in the most precise way you have.
  • Put all data that isn't really related to test in decorators out to the test method.
Michele d'Amico
  • 22,111
  • 8
  • 69
  • 76
  • Wait. I checked again. My test passes. But why patching just TotalUserResponse didn't work? Why patching TotalUserResponse.get_aggregated_actions work? Please explain – Hussain Sep 18 '15 at 12:09
  • Ok I figured out what was it is and change my answer to explain. – Michele d'Amico Sep 18 '15 at 12:12
  • Great. Upvoted. I still wonder why I was getting that AssertionError? And what can be done with that patch on TotalUserResponse? – Hussain Sep 18 '15 at 12:21
  • I guess this answers to my confusions somewhat. http://stackoverflow.com/questions/8180769/mocking-a-class-mock-or-patch – Hussain Sep 18 '15 at 12:27
  • Assertion error is because `MagicMock` is not equal to a tuple. And patch `TotalUserResponse` means that patch the _factory_ that generate `TotalUserResponse` objects: if you try to create a new one you will get a `MagicMock` instead. – Michele d'Amico Sep 18 '15 at 12:27
  • Great insight! Have accepted your answer. I was wondering if you could also me out on this other issue http://stackoverflow.com/q/32710871/1637867 – Hussain Sep 22 '15 at 17:19