1

I realize this is a pretty weird thing to want to do - it's mainly just to simplify my unittests.

I have a class whose __init__ takes a filename as an argument, which it open()s and reads a bunch of data from. It'd be really awesome if I could somehow "trick" that open() into reading from a string object instead without actually writing a temporary file to disk.

Is this possible, and if so, how would I go about it?

Schilcote
  • 2,344
  • 1
  • 17
  • 35
  • 1
    Just change the definition of `__init__`. Anything else (tempfile, replacing globals, or pipes) would be a terrible hack. – o11c Apr 28 '15 at 18:09
  • If you really can't change `__init__` (why not? why shouldn't non-unit-testing callers be able to easily pass arbitrary data instead of a filename?), this question may be a duplicate of http://stackoverflow.com/questions/5237693/mocking-openfile-name-in-unit-tests . – Random832 Apr 28 '15 at 18:22

2 Answers2

1

You can monkey-patch the module that contains the class before running the test, and restore it after the test:

def my_fake_open(fake_filename):
    return object_with_read_and_close_that_will_feed_correct_data()

def test_something(self):
    module_containing_test.open = my_fake_open
    ...run test...
    del module_containing_test.open

Check out the mock library if you don't want to write your own mock objects.

Ethan Furman
  • 63,992
  • 20
  • 159
  • 237
0

Reading a bit too fast, I thought the answer would be in io.stringIO which allows you to create a file-like object with the content of a string.

But what you want, is an object that, passed to the standard open function would actually yield a file-like object from the contents of the your string.

The thing is that open takes a string (or a file descriptor) but anything else will pause a problem.

So I don't believe this way is practical.

But actually, it's not difficult to create a file using tempfile:

with tempfile.NamedTemporaryFile() as tmp_file:
    tmp_file.write(your_string)
    yourmodule.YourClass(tmp_file.name)

(If you're on Windows you might want to play with delete=False and close before sending the name for it to be opened.)

Another approach might be to just change the API: if all the init does with the name is to open a file, why not directly pass a file-like object.

Francis Colas
  • 3,459
  • 2
  • 26
  • 31
  • That's *entirely* unhelpful. I'm trying to trick `open()` into producing an `io.stringIO` that is actually backed by a `str` or `bytearray` or whatever instead of a filesystem file - for the purposes of this question I can't alter the code I'm calling into. It's probably not possible, but I thought I'd ask. – Schilcote Apr 28 '15 at 17:38