[TIP] Is this the best way to mock out a call to with open('foo') as f?

Gregory P. Smith greg at krypto.org
Sat Oct 2 14:31:11 PDT 2010


On Sat, Oct 2, 2010 at 1:34 PM, W. Matthew Wilson <matt at tplus1.com> wrote:

> I'm using the mock package version 7.  Here's the production code I
> want to test:
>
>    def to_html(self, filepath):
>        """
>        Write this bag out as HTML to a file at filepath.
>        """
>
>        with open(os.path.join(filepath, self.html_filename), 'w') as f:
>            f.write(self.html)
>
> And here's the test for that to_html method.  I'm using mock.patch to
> substitute a MagicMock object in for the results of the call to
> open(...).
>
>    @mock.patch(
>        '__builtin__.open',
>        mock.Mock(return_value=mock.MagicMock(spec=file)))
>    def test_to_html():
>
>        global b
>        b.to_html('bogus filepath')
>
> It took me a long time to figure out how to do this.  And that makes
> me think maybe there's a much simpler and cleaner approach out there.
>
> My example is different than the one in the documentation
> (http://www.voidspace.org.uk/python/mock/magicmock.html) because that
> one looks like this:
>
> >>> from mock import Mock
> >>> mock = Mock()
> >>> mock.__enter__ = Mock()
> >>> mock.__exit__ = Mock()
> >>> mock.__exit__.return_value = False
> >>> with mock:
> ...     pass
>
> In that one, mock needs to support calls to __enter__ and __exit__.
>
> where this one is mine:
>
>    with open(...) as f:
>
> In mine, the open function needs to return something that can support
> calls to __enter__ and __exit__.
>
> So, that's why I'm making a regular mock.Mock object that then returns
> a fancy mock.MagicMock(spec=file) object.
>
> Like I said, it took me a long time to figure this out, and I find it
> confusing to read, so I hope there's some more elegant solution out
> there.
>
> Great library, by the way.
>
> Matt
>
>
I tend to dislike mocking out anything in __builtins__ as that can really
interfere with other code including code in the test harness at times
depending on what exactly it is doing.

When possible, assuming you control the code that is being tested, I
recommend refactoring the original code as such:

def to_html(self, filepath, _open=open):
   ...
   with _open(...) as x:
     ...

To test this method, pass in your own fake or mock open function that
returns an appropriate stringio for testing that you can validate the
contents of later.

-gps
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.idyll.org/pipermail/testing-in-python/attachments/20101002/0bb8ece3/attachment.html>


More information about the testing-in-python mailing list