For a specific test-scenario i wanted:

  1. avoid accessing the filesystem through pythons builtin open-function
  2. don't want to use 3rd party libraries like Michael Foord's Mock library

I would like to hear your opinion, if this seems like a valuable solution, or if there are

  • similar solutions with less lines of code
  • grave errors or caveats in the provided solution

UPDATE: After some real helpful and insightful remarks from Winston Ewert, i corrected my code:

2nd UPDATE: Eliminated superflous code and incorporated try/finally clause.

 
   from io import StringIO

   class MockFile(StringIO):
    """Wraps StringIO"""
    _vfs = {} #virtual File-System
    name = None #overwrite TextIOWrapper-property - part 1/2
    def __init__(self, name, mode = 'r', buffer_ = ''):
        self.name = name #overwrite TextIOWrapper-property - part 2/2
        if self._vfs.has_key(name):
            buffer_ = self._vfs.get(name, buffer_)
        super(MockFile, self).__init__(unicode(buffer_))

    def close(self):
        self._vfs[self.name] = self.getvalue()
        super(MockFile, self).close()

def replace_builtin(builtinname, replacementobj):
    import __builtin__
    builtin_func = getattr(__builtin__, builtinname)

    def decorator_factory(func):
        def decorator(*args, **kwarg):
            setattr(__builtin__, builtinname, replacementobj)
            try:
                result = func(*args, **kwarg)
            finally:
                setattr(__builtin__, builtinname, builtin_func)
            return result
        #attach the original name and function to the decorator:
        decorator.replaced_builtin = (builtinname, builtin_func)
        return decorator
    return decorator_factory
link|improve this question
feedback

1 Answer

up vote 1 down vote accepted
from io import StringIO

class MockFile(StringIO):
    """Wraps StringIO, because of python 2.6: clumsy name-property reset"""

    name = None #overwrite TextIOWrapper-property - part 1/2
    _vfs = {} #virtual File-System enables to "re-read" already written MockFiles

Storing files here causes them to be persistent between unit tests.

    def __init__(self, name, mode = 'r', buffer_ = None):
        self.name = name #overwrite TextIOWrapper-property - part 2/2
        if self._vfs.has_key(name):
            buffer_ = self._vfs[name]

I'd use buffer_ = self._vfs.get(name, buffer_)

        super(MockFile, self).__init__(unicode(buffer_))

What _buffer is None at this point? Won't that have strange effects?

        __enter__ = lambda self: self
        __exit__ = lambda self, exc_type, exc_value, traceback: None
        read = lambda self, size = None: super(Mockfile, self).read(size)

None of these lines of code do anything. They assign to local variables which are thrown away at the end of the function.

    def write(self, data):
        super(MockFile, self).write(data)
        self._vfs[self.name] = self.getvalue()

I wouldn't update self._vfs for every write. I'd put that in a close function. That way your tests fail if you forget to invoke close.

def replace_builtin(builtinname, replacementobj):
    import __builtin__
    builtin_func = getattr(__builtin__, builtinname)
    setattr(__builtin__, builtinname, replacementobj)

    def decorator_factory(func):
        def decorator(*args, **kwarg):
            return func(*args, **kwarg)

        #attach the original name and function to the decorator:
        decorator.replaced_builtin = (builtinname, builtin_func)
        return decorator
    return decorator_factory

I'm having real trouble figuring out how you would use this function.

UPDATE:

from io import StringIO

class MockFile(StringIO):
    """Wraps StringIO"""
    _vfs = {} #virtual File-System
    name = None #overwrite TextIOWrapper-property - part 1/2
    def __init__(self, name, mode = 'r', buffer_ = ''):
        self.name = name #overwrite TextIOWrapper-property - part 2/2
        if self._vfs.has_key(name):
            buffer_ = self._vfs.get(name, buffer_)

No need for the if. The second parameter to get is a default. Using get replaces checking for the key.

        super(MockFile, self).__init__(unicode(buffer_))

    def read(self, size = None):
        return super(MockFile, self).read(size)

    def write(self, data):
        super(MockFile, self).write(data)

These functions just call the superclass which happens anyways. So why include them?

    def close(self):
        self._vfs[self.name] = self.getvalue()
        super(MockFile, self).close()

def replace_builtin(builtinname, replacementobj):
    """makes it possible to mock the built-in functions like open.
    >>> @replace_builtin('open', MockFile)
    >>> def test_whatever(self, *args, **kwargs)
            with open(filename, 'r') as file:
                self.content = file_.read()
    """
    import __builtin__
    builtin_func = getattr(__builtin__, builtinname)

    def decorator_factory(func):
        def decorator(*args, **kwarg):
            setattr(__builtin__, builtinname, replacementobj)
            result = func(*args, **kwarg)

Bad things will happen if an exception is thrown by the function. I'd use a try/finally block here.

            setattr(__builtin__, builtinname, builtin_func)
            return result
        #attach the original name and function to the decorator:
        decorator.replaced_builtin = (builtinname, builtin_func)
        return decorator
    return decorator_factory
link|improve this answer
@DonQuestion, I've added a few comments. – Winston Ewert Feb 21 at 22:10
@W.E.: Thank you. Don't know why i did this nonsense in the first place. maybe because i wanted to much in the 1st place. I guess now it's just the vfs issue left. Thanks a lot! – Don Question Feb 22 at 0:14
feedback

Your Answer

 
or
required, but never shown

Not the answer you're looking for? Browse other questions tagged or ask your own question.