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

时间:2016-08-26 09:39:33

标签: 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)

然而,with mo(…) as (fd, err)会引发AttributeError: __exit__

documentation for mocking magic methods表明您应该将其用作

with mo as (fd, err):
    …

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

with open_w_error(filename, 'r') as (fd, err):
    …

后者是我想要嘲笑的。

3 个答案:

答案 0 :(得分: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)

编辑:如果您仍想调用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)

答案 1 :(得分:1)

问题是当您正在调用mo(..)时它返回的对象(tuple)上没有__enter____exit__属性

要解决此问题,请将mo分配给mo return_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

答案 2 :(得分:0)

你需要的是:

with mock.patch('__main__.opened_w_error') as mo:
    mo.__enter__ = Mock(return_value=(Mock(), None))
    mo.__exit__ = Mock(return_value=None)
    # Your code goes here