【问题标题】:Mock requests.Session.get in a nested context manager在嵌套上下文管理器中模拟 requests.Session.get
【发布时间】:2023-07-03 00:53:01
【问题描述】:

我想模拟一个函数,该函数使用在上下文管理器中创建的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()

执行时,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()

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

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

我尝试了__enter__.get.return_value 的任何可能组合

【问题讨论】:

  • main 中没有对requests 的单独引用(仅当您使用from...import 时才会发生这种情况),因此您必须修补"requests.Session" 而不是"main.requests.Session"
  • 谢谢@MrBean Bremen。如果我在main 中更改为from requests import Session 怎么办?
  • 在这种情况下,您将修补 "main.Session"
  • 谢谢@MrBeanBremen,我已经实现了你的建议。不过,我似乎无法获得 get 的返回值。
  • 好的,我添加了一个答案来阐明正确代码的外观 - 太复杂了,无法评论...

标签: python python-requests mocking python-unittest


【解决方案1】:

你说得差不多了,正确的模拟方法是:

with patch("main.Session") as mocked_session:
    mocked_session.return_value.__enter__.return_value.get.return_value.__enter__.return_value = "MOCKED"

我将把它分解为澄清。

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

结果为您获取模拟的Sessionresponse 对象,您可以设置另一个值。

如果在代码中分解它,它看起来像:

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"

【讨论】:

    最近更新 更多