Testing the error handling of a program using a Swagger Codegen API in Python

Viewed 576

This might be straightforward, but mocking as a whole really confuses me so I'm not sure what to do.

I currently have an external API that I'm working with generated by the Swagger Codegen library. I do not have the original template used to generate it, but I have a library similar to swagger's petshop example. I'm trying to test my code that interacts with the API, but I'm running into trouble testing what happens when an exception is raised by the external API.

Currently I have something like this for my tests:

mock_api = mocker.patch('<swagger_api>.ApiClient.call_api')
mock_api.side_effect = [ApiException(status=429), mocker.DEFAULT]

And my traceback is:

/home/rob/.pyenv/versions/3.6.6/lib/python3.6/unittest/mock.py:939: in __call__
    return _mock_self._mock_call(*args, **kwargs)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

_mock_self = <MagicMock name='call_api' id='140060372397864'>
args = ('/companies', 'GET', {}, [('next_page', <MagicMock name='call_api().next_page' id='140060365572248'>)], {'Accept': 'application/json'})
kwargs = {'_async': None, '_preload_content': True, '_request_timeout': None, '_return_http_data_only': True, ...}
self = <MagicMock name='call_api' id='140060372397864'>, _new_name = '', _new_parent = None
_call = call('/companies', 'GET', {}, [('next_page', <MagicMock name='call_api().next_page' id='140060365572248'>)], {'Accept'...tings=['ApiKeyAuth'], body=None, collection_formats={}, files={}, post_params=[], response_type='ApiResponseCompanies')
seen = set(), skip_next_dot = False, do_method_calls = False, name = 'call_api'

    def _mock_call(_mock_self, *args, **kwargs):
        self = _mock_self
        self.called = True
        self.call_count += 1
        _new_name = self._mock_new_name
        _new_parent = self._mock_new_parent
    
        _call = _Call((args, kwargs), two=True)
        self.call_args = _call
        self.call_args_list.append(_call)
        self.mock_calls.append(_Call(('', args, kwargs)))
    
        seen = set()
        skip_next_dot = _new_name == '()'
        do_method_calls = self._mock_parent is not None
        name = self._mock_name
        while _new_parent is not None:
            this_mock_call = _Call((_new_name, args, kwargs))
            if _new_parent._mock_new_name:
                dot = '.'
                if skip_next_dot:
                    dot = ''
    
                skip_next_dot = False
                if _new_parent._mock_new_name == '()':
                    skip_next_dot = True
    
                _new_name = _new_parent._mock_new_name + dot + _new_name
    
            if do_method_calls:
                if _new_name == name:
                    this_method_call = this_mock_call
                else:
                    this_method_call = _Call((name, args, kwargs))
                _new_parent.method_calls.append(this_method_call)
    
                do_method_calls = _new_parent._mock_parent is not None
                if do_method_calls:
                    name = _new_parent._mock_name + '.' + name
    
            _new_parent.mock_calls.append(this_mock_call)
            _new_parent = _new_parent._mock_new_parent
    
            # use ids here so as not to call __hash__ on the mocks
            _new_parent_id = id(_new_parent)
            if _new_parent_id in seen:
                break
            seen.add(_new_parent_id)
    
        ret_val = DEFAULT
        effect = self.side_effect
        if effect is not None:
            if _is_exception(effect):
                raise effect
    
            if not _callable(effect):
>               result = next(effect)
E               StopIteration

/home/rob/.pyenv/versions/3.6.6/lib/python3.6/unittest/mock.py:998: StopIteration

I don't think this is specific to the API I'm using, so I omitted those parts of the traceback and code for privacy.

What I'd essentially like to happen is:

  • Code calls API
  • First time API is called, but it raises an exception. I'd prefer not to have to mock out the entire response of the API call.
  • Code for handling the exception and retrying calls API again
  • This time API calls external API and returns response.

Note that I'm doing this for multiple API calls, so my tests are parameterized with callables, so it's not really possible to mock out the code at an earlier point of the Swagger API cycle. Removing the parameterized tests is one possibility, but I'd prefer another option.

If there's already a library out there that does this, that would be ideal.

0 Answers
Related