嵌套上下文管理器中的模拟 requests.Session.get

Val*_*Val 4 python mocking python-requests python-unittest

我想模拟一个函数requests.Session,该函数使用在上下文管理器中创建的对象来执行请求get,再次使用上下文管理器。所以涉及到两个上下文管理器。

这是一个示例代码:

# main.py

from requests import Session

def fun(session):
    with session.get("https://httpstat.us/200") as response:
        print(response)

        
def run():
    with Session() as session:
        fun(session)

        
if __name__ == "__main__":
    run()

Run Code Online (Sandbox Code Playgroud)

200执行时,会按预期打印的状态代码。

现在我想模拟 get 请求,以返回不同的值。不知怎的,我无法浏览混乱的上下文管理器和返回值。

这是我最大的努力:

# maintest.py
from unittest.mock import patch
from main import run

def test_main():
    
    with patch("main.Session") as mocked_session:
        mocked_session.__enter__.return_value.get.__enter__.return_value = "MOCKED"
        run()
Run Code Online (Sandbox Code Playgroud)

使用 pytest 运行测试,我希望得到"MOCKED"打印,但我得到了

<MagicMock name='Session().__enter__().get().__enter__()' id='140303100714240'>

__enter__我尝试过任何.get可以想象的组合.return_value

MrB*_*men 8

你几乎猜对了,正确的mock方法是:

with patch("main.Session") as mocked_session:
    mocked_session.return_value.__enter__.return_value.get.return_value.__enter__.return_value = "MOCKED"
Run Code Online (Sandbox Code Playgroud)

我将对此进行分解以进行澄清。

session_mock指的是Session班级。要获取Session实例,您可以使用return_value(就像每次调用一样)。
with语句在内部调用__enter__()该会话来获取实例session,因此您必须__enter__为方法和return_value调用添加内容。
现在您调用get()该对象,按照相同的逻辑,该对象需要添加一个get.return_value.
最后,这在另一个with语句中用于 create response,这又需要添加一个__enter__.return_value.

结果为您提供了response模拟的对象Session,您可以设置另一个值。

如果将其分解为代码,它将类似于:

with patch("main.Session") as mocked_session:
    mocked_session_instance = mocked_session.return_value
    mocked_session_object = mocked_session_instance.__enter__.return_value
    mocked_get = mocked_session_object.get.return_value
    mocked_response = mocked_get.__enter__
    mocked_response.return_value = "MOCKED"
Run Code Online (Sandbox Code Playgroud)