patternpythonMinor
Monkeypatching builtin open and File Mock-Up for unit testing
Viewed 0 times
fileopenmockmonkeypatchingbuiltintestingforandunit
Problem
For a specific test-scenario I wanted:
I would like to hear your opinion, if this seems like a valuable solution, or if there are
- avoid accessing the file system through pythons builtin
open-function
- 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
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
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]
super(MockFile, self).__init__(unicode(buffer_))
__enter__ = lambda self: self
__exit__ = lambda self, exc_type, exc_value, traceback: None
read = lambda self, size = None: super(Mockfile, self).read(size)
def write(self, data):
super(MockFile, self).write(data)
self._vfs[self.name] = self.getvalue()
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_factorySolution
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 MockFilesStoring 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_factoryI'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_factoryCode Snippets
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 MockFilesdef __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]super(MockFile, self).__init__(unicode(buffer_))__enter__ = lambda self: self
__exit__ = lambda self, exc_type, exc_value, traceback: None
read = lambda self, size = None: super(Mockfile, self).read(size)def write(self, data):
super(MockFile, self).write(data)
self._vfs[self.name] = self.getvalue()Context
StackExchange Code Review Q#9228, answer score: 4
Revisions (0)
No revisions yet.