7

I'm currently trying to mock out the open() built in method in Python for a test. However, I always end up getting a crash and this resulting message:

   File "/opt/home/venv/lib/python2.7/site-packages/nose-1.3.0-py2.7.egg/nose/result.py", line 187, in _exc_info_to_string
return _TextTestResult._exc_info_to_string(self, err, test)
 File "/opt/python-2.7.3/lib/python2.7/unittest/result.py", line 164, in _exc_info_to_string
msgLines = traceback.format_exception(exctype, value, tb)
 File "/opt/python-2.7.3/lib/python2.7/traceback.py", line 141, in format_exception
list = list + format_tb(tb, limit)
 File "/opt/python-2.7.3/lib/python2.7/traceback.py", line 76, in format_tb
return format_list(extract_tb(tb, limit))
  File "/opt/python-2.7.3/lib/python2.7/traceback.py", line 101, in extract_tb
line = linecache.getline(filename, lineno, f.f_globals)
  File "/opt/home/venv/lib/python2.7/linecache.py", line 14, in getline
lines = getlines(filename, module_globals)
 File "/opt/home/venv/lib/python2.7/linecache.py", line 40, in getlines
return updatecache(filename, module_globals)
 File "/opt/home/venv/lib/python2.7/linecache.py", line 127, in updatecache
with open(fullname, 'rU') as fp:
AttributeError: __exit__

Here is my test code:

m = mox.Mox()
m.StubOutWithMock(__builtin__, 'open')
mock_file = m.CreateMock(__builtin__.file)

open(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn(mock_file)
mock_file.write(mox.IgnoreArg()).MultipleTimes()
mock_file.close()

write_file_method()
user2253332
  • 797
  • 3
  • 12
  • 21

1 Answers1

4

__exit__ is the method that gets called when you try to close a file. Your mock file does not handle mock_file.close(), just open(). You'll need to mock the close method too.


Edit:

On second though, why do you want to mock open? AFAIK you shouldn't be doing that method. The method under test should take an open stream (instead of a filename, for instance). In production code, clients are responsible for opening a file (e.g. pickle.dump). In your tests, you pass in a StringIO, or a mock object that supports writing.


Edit 2: I would split your method in two and test each bit separately.

  • creating a file: check that prior to calling this method the file does not exist, and it does after that. One might argue such a one-line method isn't worth testing.
  • writing to a file: see above. Create a StringIO and write to that, so your tests can then verify the correct thing has been written.
mbatchkarov
  • 15,487
  • 9
  • 60
  • 79
  • So I add a line saying m.StubOutWithMock(__builtin__, 'close')? I tried that but I get the same error, unless I put it in the wrong place (right after m.StubOut 'open') – user2253332 Jul 01 '14 at 14:47
  • The method I want to test makes a file and writes to it, so I don't pass in a file object or anything. I'm not sure how else to test it. – user2253332 Jul 01 '14 at 15:00
  • 3
    I don't think it's the `open` / `close` interface, at least not yet. It looks like the problem is he's not mocking the context manager interface, ie `__enter__` and `__exit__`. FYI, `__exit__` isn't called when closing the file, it's the other way around. `__exit__` is called when exiting the context manager (`with` block), and it in turn calls `close`. It also looks like the actual error in the test code is being swallowed by `nose`, because `nose` is trying to open a file to write an error to with the context manager syntax, but the patched `open` does not support it. – Silas Ray Jul 01 '14 at 15:06
  • To mox out `__enter__` and `__exit__`, it is `self.__mox.StubOutWithMock(__builtin__, '__enter__')` and such? – user2253332 Jul 01 '14 at 15:10
  • Those are methods on the object returned from `open`, not things from `__builtin__` directly. Honestly though, I think you should try to find another approach. You're going to break your test logging this way. Maybe try writing a wrapper or something that patches locals/globals around your specific target function, so you don't impact other libraries relying on `open` outside of the code under test. – Silas Ray Jul 01 '14 at 15:15
  • @SilasRay Thanks for the correction, you are right. I've updated my answer again :) – mbatchkarov Jul 01 '14 at 15:19