Reputation: 3010
I have a class which I instantiate by giving a file name like parser = ParserClass('/path/to/file')
, then I call parser.parse()
method which opens and reads the file.
Now I want to unit test that if something bad happening inside:
with open(filename, 'rb') as fp:
// do something
the correct Exception will be raised, so I want to mock the __builtin__.open
like this:
from mock import MagicMock, patch
from StringIO import StringIO
test_lines = StringIO("""some test lines, emulating a real file content""")
mock_open = MagicMock(return_value=test_lines)
with patch('__builtin__.open', mock_open):
self.mock.parse()
but this gives me an AttributeError: StringIO instance has no attribute '__exit__'
.
I tought StringIO behaves exactly like a file object, but it seems, this is not the case.
How could I test this method with a given content (test_lines) with mock objects? What should I use instead?
Upvotes: 9
Views: 14911
Reputation: 8287
There is a provision, specifically for this purpose in the mock
library:
It does have an issue in missing support for default iterator (ie. __iter__
method, so you can't do for line in opened_mock_file
straight away), but it can be worked around as described here.
I have upvoted @icecrime's answer, but I feel his update part doesn't seem to be highlighted enough.
Upvotes: 1
Reputation: 76755
This is a known issue that StringIO
does not implement the context manager protocol.
A common recipe is the following:
from contextlib import contextmanager
@contextmanager
def StringIO():
"""Add support for 'with' statement to StringIO - http://bugs.python.org/issue1286
"""
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
sio = StringIO()
try:
yield sio
finally:
sio.close()
It implements the context manager protocol for StringIO
and allows it to be used in a with
statement.
UPDATE Well, I just discovered the existence of mock_open
which can directly read from a string, so it's probably the way to go.
Upvotes: 8
Reputation: 531165
You could subclass StringIO
to provide a context manager:
class ContextualStringIO(StringIO):
def __enter__(self):
return self
def __exit__(self, *args):
self.close() # icecrime does it, so I guess I should, too
return False # Indicate that we haven't handled the exception, if received
test_lines = ContextualStringIO(...)
Gross speculation: if StringIO
objects are drop-in replacements for file
objects except for the lack of a context manager, I wonder if this would work:
class ContextualStringIO(StringIO, file):
pass
ContextualStringIO
inherits what file operations it can from StringIO
, but everything else is inherited from file
. It looks elegant, but probably requires extensive testing (or someone more familiar with Python internals to explain why this wouldn't work).
Upvotes: 10
Reputation: 3010
Alternatively, you can use the io.StringIO
from the Standard Library:
The io module provides the Python interfaces to stream handling. Under Python 2.x, this is proposed as an alternative to the built-in file object, but in Python 3.x it is the default interface to access files and streams.
However (as the Note says) this can be used only with unicode
type.
Upvotes: 2