上下文管理器的模拟失败,出现AttributeError:__ exit__

Sar*_*ica 4 python mocking

我正在尝试使用Mock修补一些上下文管理器功能,因此我可以测试代码在给出好,坏和垃圾输入的情况下做出明智的事情.这是包含with声明的测试代码.补丁在我的代码中的正确位置完成.

@patch("__main__.opened_w_error")
def test_get_recipe_file(self, mo):
    mo.return_value = (Mock(), None)
    mo.__enter__ = Mock(return_value=None)
    mo.__exit__ = Mock(return_value=None)
    with mo(…) as (fd, err):  # AttributeError: __exit__ is raised here.
        print(fd)
        print(err)
Run Code Online (Sandbox Code Playgroud)

然而with mo(…) as (fd, err)加薪AttributeError: __exit__.

模拟魔术方法文档说明你应该使用它

with mo as (fd, err):
    …
Run Code Online (Sandbox Code Playgroud)

后一段代码是我试图嘲笑的.但这不是我在代码中使用它的方式.对于那些真正感兴趣的人,我试图在PEP 343中模拟示例6 opened_w_error(),它处理打开文件和捕获错误.因此代码是:

with open_w_error(filename, 'r') as (fd, err):
    …
Run Code Online (Sandbox Code Playgroud)

后者是我想要嘲笑的.

Tim*_*chs 5

请注意,传递给with语句的对象应该具有的对象__enter____exit__方法,以及__enter__用于as构造的返回值.在您的情况下,您正在调用mo(...),返回(Mock(), None),这不是上下文管理器.您应该将此返回值移动到该__enter__方法.

@patch("__main__.opened_w_error")
def test_get_recipe_file(self, mo):
    mo.__enter__ = Mock(return_value=(Mock(), None))
    mo.__exit__ = Mock(return_value=None)
    with mo as (fd, err):
        print(fd)
        print(err)
Run Code Online (Sandbox Code Playgroud)

编辑:如果您仍想调用mo,则将其返回值设置为上下文管理器.

@patch("__main__.opened_w_error")
def test_get_recipe_file(self, m_opened_w_error):
    mo = Mock()
    mo.__enter__ = Mock(return_value=(Mock(), None))
    mo.__exit__ = Mock(return_value=None)
    m_opened_w_error.return_value = mo
    with m_opened_w_error(...) as (fd, err):
        print(fd)
        print(err)
Run Code Online (Sandbox Code Playgroud)


Ash*_*ary 5

问题是当你调用mo(..)它返回的对象时( tuple) 没有__enter____exit__属性。

要修复此分配momo'sreturn_value以便仍然可以找到您在其上设置的上下文管理器属性。

@patch("__main__.opened_w_error")
def test_get_recipe_file(self, mo):
    mo.return_value = mo
    mo.__enter__ = Mock(return_value=(Mock(), None))
    mo.__exit__ = Mock(return_value=None)
    with mo('file', 'r') as (fd, err): 
        print(fd)
        print(err)
        mo.assert_called_once_with('file', 'r')  # Should be True
Run Code Online (Sandbox Code Playgroud)