Mock for context-manager fails with AttributeError: __exit__
<p>I am trying to patch some context manager function using a Mock so I can test that the code does sensible things given good, bad, and garbage input. Here is the test code with the <code>with</code> statement in it. The patch is done in the correct place in my code.</p>

<pre><code>@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)
</code></pre>

<p>However the <code>with mo(â¦) as (fd, err)</code> raises <code>AttributeError: __exit__</code>.</p>

<p>The <a href="http://www.voidspace.org.uk/python/mock/magicmock.html?highlight=__exit__" rel="nofollow">documentation for mocking magic methods</a> states the you should use it as</p>

<pre><code>with mo as (fd, err):
    â¦
</code></pre>

<p>The latter piece of code is what I am trying to mock.
 but that is not how I use it in my code. For those really interested, I am trying to mock <a href="https://www.python.org/dev/peps/pep-0343/" rel="nofollow">example 6 <code>opened_w_error()</code> in PEP 343</a> which deals with opening files and catching errors. Thus the code is:</p>

<pre><code>with open_w_error(filename, 'r') as (fd, err):
    â¦
</code></pre>

<p>The latter is what I am trying to mock.</p>

<p>Note that the object you pass to the <em>with</em> statement is the one that should have <code>__enter__</code> and <code>__exit__</code> methods, with the return value from <code>__enter__</code> used for the <code>as</code> construct. In your case, you are calling <code>mo(...)</code>, which returns <code>(Mock(), None)</code>, and this is not a context manager. You should move this return value to the <code>__enter__</code> method.</p>

<pre><code>@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)
</code></pre>

<p><strong>EDIT</strong>: If you still want to call <code>mo</code>, then make its return value a context manager.</p>

<pre><code>@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)
</code></pre>

python mocking