Reputation: 8488
There's a couple of stack overflow posts out there talking about mocking the open call in Python. That's great but it doesn't really help me if a function takes in a file handle or stream object instead of a file path.
One solution I've been using up until now has been cStringIO
objects. I've run into a problem, however.
If I want to test if I'm logging the file name correctly on some sort of failure (say if the file / stream is empty and you expect some kind of data)
cStringIO
fd = cStringIO("")
fd.name = "testing/path" # Throws an AttributeError
I can't set the name attribute since cStringIO
and StringIO
are slotted classes.
If switch over to using open_mock
with mock.patch('__main__.open', mock.mock_open(read_data=''), create=True) as m:
I run into
AttributeError: Mock object has no attribute 'tell'
At this point it feels like I have to use temp files but I'd like to avoid actually calling out to the file system if possible.
How do you test functions that take in file handles without having to create actual files on a file system?
Upvotes: 1
Views: 1829
Reputation: 8488
I went down the path of creating a class that inherited from StringIO. It took me more time than I want to admit to figure out that in Python2.7 StringIO is an old style class.
class MockFile(StringIO, object):
"""This is a work around for the fact that StringIO is a slotted class and
doesn't have a name attribute.
"""
name = None
def __init__(self, name, buffer_ = None):
super(MockFile, self).__init__(buffer_)
self.name = name
Upvotes: 0
Reputation: 369054
You can set the tell
attribute explicitly for the mock object using Mock.return_value
:
import mock
def function_under_test(f):
f.tell() # => 0
f.read()
f.tell() # => 0
return f.name
with mock.patch('__main__.open', mock.mock_open(read_data=''), create=True) as m:
with open('/tmp/1') as f:
f.name = '/tmp/1'
f.tell.return_value = 0
assert function_under_test(f) == '/tmp/1'
Upvotes: 1