Permalink
Browse files

ensure mock anchors cant shadow other mock anchors

  • Loading branch information...
1 parent d2e12d0 commit a15d3aa3e947936d96b34ec9525fb2237ca1e2b1 @timbertson committed Jan 3, 2009
Showing with 41 additions and 4 deletions.
  1. +23 −4 mocktest/mockanchor.py
  2. +18 −0 test/mock_anchor_test.py
View
@@ -23,12 +23,20 @@ def mock_on(parent, quiet = False):
class MockAnchor(RealSetter):
_active = []
- def __init__(self, parent, quiet=False):
+ def __new__(cls, parent, quiet=False):
+ for anchor in cls._active:
+ if anchor._parent is parent:
+ return anchor
+
+ # this should use super, but I've no idea how...
+ supertype = RealSetter
+ self = supertype.__new__(cls)
self._init_records()
self._real_set(_parent = parent)
self._real_set(_quiet = quiet)
self.__class__._active.append(self)
-
+ return self
+
def _init_records(self):
self._real_set(_children = {})
self._real_set(_items = {})
@@ -44,7 +52,7 @@ def _real_child_store(self, in_dict):
def _backup_child(self, name, in_dict):
try:
# if it's replacing a real object, store it here for later
- real_child = getattr(self._parent, name) if not in_dict else self._parent[name]
+ real_child = self._accessor_func(in_dict)(self._parent, name)
except (AttributeError, KeyError):
if not self._quiet:
print >> sys.stderr, "Warning: object %s has no %s \"%s\"" % (self._parent, "key" if in_dict else "attribute", name)
@@ -70,12 +78,15 @@ def _reset_all(cls):
mock._reset()
cls._active = []
- # interchangeable deletion and setter methods
+ # interchangeable deletion, getter and setter methods
def _insertion_func(self, in_dict):
return self._setitem if in_dict else self._setattr
def _deletion_func(self, in_dict):
return self._delitem if in_dict else self._delattr
+
+ def _accessor_func(self, in_dict):
+ return self._getitem if in_dict else self._getattr
@staticmethod
def _setitem(target, name, val):
@@ -84,6 +95,14 @@ def _setitem(target, name, val):
@staticmethod
def _setattr(target, name, val):
setattr(target, name, val)
+
+ @staticmethod
+ def _getitem(target, name):
+ return target[name]
+
+ @staticmethod
+ def _getattr(target, name):
+ return getattr(target, name)
@staticmethod
def _delitem(target, name):
View
@@ -40,6 +40,17 @@ def test_should_attach_new_mocks_to_parent(self):
self.assertTrue(isinstance(real_dict['c'], mock_class))
self.assertTrue(isinstance(real_object.c, mock_class))
+ def test_should_not_clobber_anchors_for_the_same_parent(self):
+ anchor_a = mock_on(real_object)
+ anchor_a.foo
+ self.assertEqual(anchor_a._children.keys(), ['foo'])
+
+ anchor_b = mock_on(real_object)
+ self.assertEqual(anchor_b._children.keys(), ['foo'])
+
+ self.assertTrue(anchor_a, anchor_b)
+ self.assertTrue(isinstance(anchor_a, mocktest.mockanchor.MockAnchor))
+
def test_should_reinstate_original_objects_on_teardown(self):
mock_on(real_object).a = 'mocky a'
mock_on(real_dict)['a'] = 'mocky a'
@@ -58,6 +69,13 @@ def test_should_delete_original_objects_if_they_didnt_exist_before_the_mock(self
self.assertRaises(AttributeError, lambda: real_object.c)
self.assertRaises(KeyError, lambda: real_dict['c'])
+ def test_should_disallow_replacing_mocks(self):
+ obj = RealClass()
+ obj.foo = mock_wrapper().mock
+ def set_foo():
+ mock = mock_on(obj).foo
+ self.assertRaises(TypeError, set_foo)
+
def test_should_set_original_objects_to_none_if_they_cant_be_deleted(self):
obj = NoDeletesObject()
dict_ = NoDeletesDict()

0 comments on commit a15d3aa

Please sign in to comment.