4 ms·
Super Simple Mocking For Python
- stch 15y agoDoesn't work as expected when (a) the attribute to be mocked is not part of the module originally (the attribute persists in the symbol table after the mock is destroyed) and (b) if one of the attributes to be mocked is named 'orig'.
- jacobolus 15y agoself.mocks = dict(zip(args[::2], args[1::2])) Makes for a very unpythonic feeling API. I’d be interested to hear why the author settled on this design, and see example uses that he likes the look of. Are there any other libraries which use this pattern?
- tebeka 15y agoYou can either use mock(obj, {'x': 1, 'y': 2}) or mock(obj, 'x', 1, 'y', 2). I thought it'll be nice to provide both options, but you might be right and using just a dict of mocks is more Pythonic.
- MBlume 15y agoAt risk of bikeshedding, I think the "most pythonic" approach would be mock(obj, x=1, y=2) Seriously though, cool lib, thanks for it =)
- jacobolus 15y agoYes, the most pythonic is: mock(obj, x=1, y=2) Which you get by: def __init__(self, obj, **mocks): self.obj = obj self.mocks = mocks This can then also be called (using the built-in double-star-unpacking feature) as: mock(obj, **{'x': 1, 'y': 2}) or even mock(obj, **dict([('x', 1), ('y', 2)])) Then there’s no explicit need to support multiple alternative function signatures, and in particular you don’t get people using the (quite atypical and therefore semantically ambiguous): mock(obj, 'x', 1, 'y', 2).
- tebeka 15y agoThanks, updated the code to use keyword arguments only.
- has207 15y agoIt'd be really nice if it were this simple, and in a language like Javascript you'd probably succeed. But Python has a lot of pit traps for you when you start monkey patching things. For example, what happens when the attribute you're mocking (though really you should be calling it stubbing since that's all that happens here) is inherited from a superclass and you try to restore it with your dict update approach? What happens to all the other instance of the class? I'd stick with an existing library that does these things correctly.
- johnrob 15y agoThe line where the original attributes are backed up needs to be a deep copy (otherwise the updates will affect the backed up dictionary): self.orig = self.obj.__dict__ Should be more like: self.orig = self.obj.__dict__.copy()
- surething 15y agoAgreed. I did the following: def __enter__(self): self.original = dict(self.obj.__dict__) self.obj.__dict__.update(self.mocks) return self def __exit__(self, typ, val, traceback): self.obj.__dict__ = self.original This doesn't perform a deep copy, but prevents modifications on self.obj.__dict__ from modifying self.original also.
- noidi 15y agoFor another clever take on mocking in Python, check out Ian Bicking's minimock: http://blog.ianbicking.org/minimock.html http://blog.ianbicking.org/minimock.html
- paolovictor 15y agoI'm getting more intimate with Python and also made a small mocking library, I'm looking for constructive feedback: https://github.com/paolovictor/mockaccino https://github.com/paolovictor/mockaccino