Как имитировать атрибуты и свойства возвращаемых значений с помощью patch()

test_client/wclient.py

import json
import requests
client = requests.session()

def setup():
    response = REST_CLIENT.post(
        "https://placeholder.com",
        auth=(placeholder, placeholder),
        data={"grant_type": "client_credentials"},
    )

    status_code = response.status_code
    if status_code in OK_STATUS:
        payload = json.loads(response.content, object_pairs_hook=OrderedDict)
    else:
        payload = response.text
        msg = (
            "Status Code %s" % status_code
        )
        logger.error(msg)
        raise ValueError(msg)

    return payload["access_token"]

Тестовый файл: test_client/test_client.py

import mock
import wclient
@mock.patch("test_client.wclient")
def test_taxes_pitney_bowes_setup_success(resp):
    resp.return_value.post.return_value.status_code = "200"
    wclient.pitney_bowes_setup()

Status Code <MagicMock name='REST_CLIENT.post().status_code' id='4868492200'>

Как я могу издеваться над методами и атрибутами модуля с помощью mock.patch()? Я прочитал страницы сообщений о переполнении стека, но я запутался со всеми различными способами принудительного применения магического макета.

Я пробовал издеваться:

resp.return_value.post.return_value.status_code
resp.return_value.post.return_value.status_code.return_value
resp.post.return_value.status_code
resp.post.return_value.status_code.return_value
resp.post.status_code
resp.post.status_code.return_value

person Rhubarrbb    schedule 01.08.2019    source источник


Ответы (1)


Я думаю, что существует множество способов на самом деле сделать макет (см. Многие методы в Вызовы методов имитации в Python ). Мне нравится это делать и легко находить простые макеты:

Для функций: @patch('module.print', lambda x: None)

Для атрибутов: @patch('module.cwd', os.path.join(os.getcwd(), "folder"))

Эта запись в блоге может быть вам полезна: https://medium.com/uckey/how-mock-patch-decorator-works-in-python-37acd8b78ae.

Дайте мне знать, если у вас есть дополнительные вопросы.

Изменить: чтобы добавить несколько макетов, просто добавьте еще один атрибут:

import wclient
@mock.patch("test_client.wclient")
@mock.patch("another_attribute", "value")
@mock.patch("another_function", lambda x, y: x + y)
def test_taxes_pitney_bowes_setup_success(resp):
    resp.return_value.post.return_value.status_code = "200"
    wclient.pitney_bowes_setup()

person OrionTheHunter    schedule 01.08.2019
comment
Сообщения в блоге помогают моему общему пониманию, но я не очень понимаю, как это помогает решить текущую проблему. @patch('module.print', lambda x: None) установит значение функции, не так ли? Редактировать: Случайно нажал на сохранение правок. Продолжая, что, если бы я хотел сохранить общий объект mock, но иметь возможность возвращать значения для нескольких атрибутов, помимо status_code? Что, если я захочу сделать это для status_code и text? Что, если бы я хотел иметь возможность издеваться над обоими сообщениями и использовать один и тот же метод? - person Rhubarrbb; 02.08.2019
comment
Правильно, это изменит функцию печати в модуле, чтобы она возвращала None. Вы можете использовать это, чтобы вернуть любое значение и смоделировать функцию, или использовать его для изменения переменной на определенное значение (см. атрибут mock). Изменить: вы можете добавить несколько исправлений над тестом, чтобы указать различные атрибуты и функции. Вы также можете изменить эту лямбду, чтобы она принимала несколько аргументов и возвращала несколько аргументов на основе этого ввода. Я могу добавить этот пример к ответу выше. - person OrionTheHunter; 02.08.2019
comment
Вы говорите, что для издевательства как над статусом_кодом, так и над текстом для объекта сообщения мне нужно было бы издеваться над методом сообщения? Или вы предлагаете мне издеваться над атрибутами status_code и text? - person Rhubarrbb; 02.08.2019
comment
В продолжение нового, отредактированного поста. В этом сценарии объект MagicMock — это resp. С несколькими декораторами mock.patch над чем эффективно издевается resp? Это test_client.wclient? Как один объект может учитывать более одного совпадения? РЕДАКТИРОВАТЬ: я понимаю, что another_attribute и another_function имеют свои возвращаемые значения в декораторе. Виноват! Но это не исправляет код состояния, который не был должным образом издевался. - person Rhubarrbb; 02.08.2019
comment
Я не думаю, что ваше решение отвечает на мой вопрос. Ни один из них не подходит для данной ситуации. - person Rhubarrbb; 02.08.2019
comment
Возможно я не правильно понимаю вопрос. Не могли бы вы уточнить вопрос? Я думал, что вопрос в том, как издеваться над методами и атрибутами модуля с помощью mock.patch()? Вы специально хотите издеваться над status_code? Я не знаком с объектом response, но из вашего фрагмента кода кажется, что вы должны издеваться над @mock.patch("<module_name>.response.status_code", 200) Изменить: похоже, имя вашего модуля test_client - person OrionTheHunter; 02.08.2019
comment
Я не думаю, что вы можете исправить ответ, потому что это не в пространстве имен. Это локальная переменная внутри функции. Я хочу издеваться над возвращаемым значением requests.session, у которого есть атрибуты. По сути, смоделируйте requests.session и атрибуты возвращаемых значений двух его методов, GET и POST. - person Rhubarrbb; 02.08.2019
comment
@Rhubarrbb Попробуйте найти, какой объект возвращают его методы, и исправьте это. Например. если request.session возвращает экземпляр Session из библиотеки запросов, исправьте 'requests.Session' и его возвращаемые значения в своем тесте. - person LaundroMat; 30.11.2020