Getting an actual return value for a mocked file.read()

22,493

Solution 1

This sounds like a good use-case for a StringIO object that already implements the file interface. Maybe you can make a file_mock = MagicMock(spec=file, wraps=StringIO('test')). Or you could just have your function accept a file-like object and pass it a StringIO instead of a real file, avoiding the need for ugly monkey-patching.

Have you looked the mock documentation?

http://www.voidspace.org.uk/python/mock/compare.html#mocking-the-builtin-open-used-as-a-context-manager

Solution 2

In Python 3 the pattern is simply:

>>> import unittest.mock as um
>>> with um.patch('builtins.open', um.mock_open(read_data='test')):
...     with open('/dev/null') as f:
...         print(f.read())
...
test
>>>

(Yes, you can even mock /dev/null to return file contents.)

Solution 3

building on @tbc0 answer, to support Python 2 and 3 (multi-version tests are helpful to port 2 to 3):

import sys
module_ = "builtins"
module_ = module_ if module_ in sys.modules else '__builtin__'

try:
    import unittest.mock as mock
except (ImportError,) as e:
    import mock 

with mock.patch('%s.open' % module_, mock.mock_open(read_data='test')):
    with open('/dev/null') as f:
        print(f.read())
Share:
22,493
Brian Hicks
Author by

Brian Hicks

Updated on July 08, 2020

Comments

  • Brian Hicks
    Brian Hicks almost 4 years

    I'm using python-mock to mock out a file open call. I would like to be able to pass in fake data this way, so I can verify that read() is being called as well as using test data without hitting the filesystem on tests.

    Here's what I've got so far:

    file_mock = MagicMock(spec=file)
    file_mock.read.return_value = 'test'
    
    with patch('__builtin__.open', create=True) as mock_open:
        mock_open.return_value = file_mock
    
        with open('x') as f:
            print f.read()
    

    The output of this is <mock.Mock object at 0x8f4aaec> intead of 'test' as I would assume. What am I doing wrong in constructing this mock?

    Edit:

    Looks like this:

    with open('x') as f:
         f.read()
    

    and this:

    f = open('x')
    f.read()
    

    are different objects. Using the mock as a context manager makes it return a new Mock, whereas calling it directly returns whatever I've defined in mock_open.return_value. Any ideas?

  • Brian Hicks
    Brian Hicks over 12 years
    I tried messing around some with this. It appears that mock_open is return a different object when used in a context manager versus being called directly. I've updated my question. Any ideas?
  • Brian Hicks
    Brian Hicks over 12 years
    I read the top half of that page, but skipped the bottom half for some unknown reason. Herp and derp.
  • Godfrey
    Godfrey almost 4 years
    You can use the six library to import builtins six.readthedocs.io/…
  • Vad1mo
    Vad1mo over 2 years
    If the website would be still up to this date, that would be helpful. Now the answer becomes a bit useless.