Как смоделировать метод, который делает несколько запросов POST и GET, требующих разных данных ответа?

Я просмотрел Как имитировать REST API и прочитал ответы, но я все еще Кажется, я не могу понять, как я буду работать с методом, который выполняет несколько запросов GET и POST. Вот часть моего кода ниже.

У меня есть класс, UserAliasGroups(). Его метод __init__() выполняет requests.post() для входа во внешний REST API. В моем модульном тесте этот код обрабатывает насмешку над логином, и он работает, как и ожидалось.

    @mock.patch('aliases.user_alias_groups.requests.get')
    @mock.patch('aliases.user_alias_groups.requests.post')
    def test_user_alias_groups_class(self, mock_post, mock_get):
        init_response = {
            'HID-SessionData': 'token==',
            'errmsg': '',
            'success': True
        }
        mock_response = Mock()
        mock_response.json.return_value = init_response
        mock_response.status_code = status.HTTP_201_CREATED
        mock_post.return_value = mock_response

        uag = UserAliasGroups(auth_user='TEST_USER.gen',
                              auth_pass='FakePass',
                              groups_api_url='https://example.com')
        self.assertEqual(uag.headers, {'HID-SessionData': 'token=='})

Я также определил несколько методов, таких как obtain_request_id(), has_group_been_deleted(), does_group_already_exists() и другие. Я также определяю метод create_user_alias_group(), который вызывает obtain_request_id(), has_group_been_deleted(), does_group_already_exists() и другие.

У меня также есть код в моем модульном тесте, который имитирует запрос GET к REST API для проверки моего метода has_group_been_deleted(), который выглядит следующим образом:

        has_group_been_deleted_response = {
            'error_code': 404, 
            'error_message': 'A group with this ID does not exist'
        }
        mock_response = Mock()
        mock_response.json.return_value = has_group_been_deleted_response
        mock_response.status_code = status.HTTP_404_NOT_FOUND
        mock_get.return_value = mock_response

Теперь я могу перейти к своему вопросу. Ниже приведена соответствующая часть моего кода.

class UserAliasGroups:

    def __init__(
            self,
            auth_user=settings.GENERIC_USER,
            auth_pass=settings.GENERIC_PASS,
            groups_api_url=settings.GROUPS_API_URL
    ):
        """ __init__() does the login to groups. """
        self.auth_user = auth_user
        self.auth_pass = auth_pass
        self.headers = None
        self.groups_api_url = groups_api_url
        # Initializes a session with the REST API service. Each login session times out after 5 minutes of inactivity.
        self.login_url = f'{self.groups_api_url}/api/login'

        response = requests.post(self.login_url, json={}, headers={'Content-type': 'application/json'},
                                 auth=(auth_user, auth_pass))
        if response.status_code is not 201:
            try:
                json = response.json()
            except:
                json = "Could not decode json."
            raise self.UserAliasGroupsException(f"Error: User {self.auth_user}, failed to login into "
                                                f"{self.login_url} {json}")

        response_json = response.json()
        self.headers = {'HID-SessionData': response_json['HID-SessionData']}

    def obtain_request_id(self, request_reason):
        payload = {'request_reason': request_reason}
        url = f'{self.groups_api_url}/api/v1/session/requests'
        response = requests.post(url=url, json=payload, headers=self.headers)
        if response.status_code is not status.HTTP_200_OK:
            try:
                json = response.json()
            except:
                json = "Could not decode json."
            msg = f'obtain_request_id() Error url={url} {response.status_code} {json}.'
            raise self.UserAliasGroupsException(msg)
        request_id = response.json().get('request_id')
        return request_id

    def has_group_been_deleted(self, group_name):
        url = f'{self.groups_api_url}/api/v1/groups/{group_name}/attributes/RESATTR_GROUP_DELETED_ON'
        response = requests.get(url=url, headers=self.headers)
        return response.status_code == status.HTTP_200_OK


    def does_group_already_exists(self, group_name):
        url = f'{self.groups_api_url}/api/v1/groups/{group_name}'
        response = requests.get(url=url, headers=self.headers)
        if response.status_code is status.HTTP_200_OK:
            # check if the group has been "deleted".
            return not self.has_group_been_deleted(group_name=group_name)
        return False

    def create_user_alias_group(
            self,
            ... long list of params omitted for brevity ...     
    ):

       if check_exists:
            # Check if group already exists or not.
            if self.does_group_already_exists(group_name):
                msg = f'Cannot create group {group_name}. Group already exists.'
                raise self.UserAliasGroupsException(msg)
       ... more code omitted for brevity ...

Мой вопрос заключается в том, как мне написать свой модульный тест для обработки нескольких вызовов requests.post() и request.get(), что приводит к разным ответам в моем методе create_user_alias_group()?

Я хочу вызвать create_user_alias_group() в своем модульном тесте, поэтому мне нужно выяснить, как смоделировать несколько вызовов requests.get() и requests.post().

Использую ли я несколько декораторов, например:

@mock.patch('aliases.user_alias_groups.obtain_request_id.requests.post')
@mock.patch('aliases.user_alias_groups.does_group_already_exists.requests.get')
@mock.patch('aliases.user_alias_groups.has_group_been_deleted.requests.get')
def test_user_alias_groups_class(self, mock_post, mock_get):
    ...

?

Спасибо за внимание к моему длинному вопросу :)


person Red Cricket    schedule 01.06.2020    source источник
comment
это просто тот факт, что вам нужны разные значения для разных вызовов? Как в 1-м вызове возвращает одно, 2-м вызове возвращает другое?   -  person tim-mccurrach    schedule 02.06.2020


Ответы (1)


Вы можете использовать mock.side_effect, который принимает итерируемый объект. Тогда разные вызовы будут возвращать разные значения:

mock = Mock()
mock.side_effect = ['a', 'b', 'c']

Таким образом, первый вызов mock возвращает "a", затем следующий "b" и так далее. (В вашем случае вы установите mock_get.side_effect).

person tim-mccurrach    schedule 01.06.2020