summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsmiddlek <smiddlek@b1010a0a-674b-0410-b734-77272b80c875>2010-07-07 17:43:04 +0000
committersmiddlek <smiddlek@b1010a0a-674b-0410-b734-77272b80c875>2010-07-07 17:43:04 +0000
commitf8770adb29be14845b407494cc0e3e1b98ce75e3 (patch)
treeaded93a6aaeb8638d12c36664f195f6dbfd06c7e
parent04786f376e2bebe44fc19422bbadaa3d68de2138 (diff)
downloadmox-f8770adb29be14845b407494cc0e3e1b98ce75e3.tar.gz
Adding a "fix" (cough, hack!) for method signature validation when
calling an unbound method with the instance as the first arg. By Steve Middlekauff git-svn-id: http://pymox.googlecode.com/svn/trunk@52 b1010a0a-674b-0410-b734-77272b80c875
-rwxr-xr-xmox.py31
-rwxr-xr-xmox_test.py53
2 files changed, 77 insertions, 7 deletions
diff --git a/mox.py b/mox.py
index ba73bd3..948973a 100755
--- a/mox.py
+++ b/mox.py
@@ -819,7 +819,7 @@ class _MockObjectFactory(MockObject):
super(_MockObjectFactory, self)._Verify()
-class MethodCallChecker(object):
+class MethodSignatureChecker(object):
"""Ensures that methods are called correctly."""
_NEEDED, _DEFAULT, _GIVEN = range(3)
@@ -843,6 +843,7 @@ class MethodCallChecker(object):
if inspect.ismethod(method):
self._args = self._args[1:] # Skip 'self'.
self._method = method
+ self._instance = None # May contain the instance this is bound to.
self._has_varargs = varargs is not None
self._has_varkw = varkw is not None
@@ -865,9 +866,9 @@ class MethodCallChecker(object):
Raises:
AttributeError: arg_name is already marked as _GIVEN.
"""
- if arg_status.get(arg_name, None) == MethodCallChecker._GIVEN:
+ if arg_status.get(arg_name, None) == MethodSignatureChecker._GIVEN:
raise AttributeError('%s provided more than once' % (arg_name,))
- arg_status[arg_name] = MethodCallChecker._GIVEN
+ arg_status[arg_name] = MethodSignatureChecker._GIVEN
def Check(self, params, named_params):
"""Ensures that the parameters used while recording a call are valid.
@@ -881,10 +882,26 @@ class MethodCallChecker(object):
Raises:
AttributeError: the given parameters don't work with the given method.
"""
- arg_status = dict((a, MethodCallChecker._NEEDED)
+ arg_status = dict((a, MethodSignatureChecker._NEEDED)
for a in self._required_args)
for arg in self._default_args:
- arg_status[arg] = MethodCallChecker._DEFAULT
+ arg_status[arg] = MethodSignatureChecker._DEFAULT
+
+ # WARNING: Suspect hack ahead.
+ #
+ # Check to see if this is an unbound method, where the instance
+ # should be bound as the first argument. We try to determine if
+ # the first argument (param[0]) is an instance of the class, or it
+ # is equivalent to the class (used to account for Comparators).
+ #
+ # NOTE: If a Func() comparator is used, and the signature is not
+ # correct, this will cause extra executions of the function.
+ if inspect.ismethod(self._method):
+ # The extra param accounts for the bound instance.
+ if len(params) == len(self._args) + 1:
+ clazz = getattr(self._method, 'im_class', None)
+ if isinstance(params[0], clazz) or params[0] == clazz:
+ params = params[1:]
# Check that each positional param is valid.
for i in range(len(params)):
@@ -906,7 +923,7 @@ class MethodCallChecker(object):
# Ensure all the required arguments have been given.
still_needed = [k for k, v in arg_status.iteritems()
- if v == MethodCallChecker._NEEDED]
+ if v == MethodSignatureChecker._NEEDED]
if still_needed:
raise AttributeError('No values given for arguments: %s'
% (' '.join(sorted(still_needed))))
@@ -956,7 +973,7 @@ class MockMethod(object):
self._side_effects = None
try:
- self._checker = MethodCallChecker(method_to_mock)
+ self._checker = MethodSignatureChecker(method_to_mock)
except ValueError:
self._checker = None
diff --git a/mox_test.py b/mox_test.py
index 2e429e6..005455a 100755
--- a/mox_test.py
+++ b/mox_test.py
@@ -1496,6 +1496,59 @@ class MoxTest(unittest.TestCase):
self.assertEquals('foo', actual)
self.assertTrue(type(test_obj.OtherValidCall) is method_type)
+ def testStubOutMethod_CalledAsUnboundMethod_Comparator(self):
+ instance = TestClass()
+ self.mox.StubOutWithMock(TestClass, 'OtherValidCall')
+
+ TestClass.OtherValidCall(mox.IgnoreArg()).AndReturn('foo')
+ self.mox.ReplayAll()
+
+ actual = TestClass.OtherValidCall(instance)
+
+ self.mox.VerifyAll()
+ self.mox.UnsetStubs()
+ self.assertEquals('foo', actual)
+
+ def testStubOutMethod_CalledAsUnboundMethod_ActualInstance(self):
+ instance = TestClass()
+ self.mox.StubOutWithMock(TestClass, 'OtherValidCall')
+
+ TestClass.OtherValidCall(instance).AndReturn('foo')
+ self.mox.ReplayAll()
+
+ actual = TestClass.OtherValidCall(instance)
+
+ self.mox.VerifyAll()
+ self.mox.UnsetStubs()
+ self.assertEquals('foo', actual)
+
+ def testStubOutMethod_CalledAsUnboundMethod_DifferentInstance(self):
+ instance = TestClass()
+ self.mox.StubOutWithMock(TestClass, 'OtherValidCall')
+
+ TestClass.OtherValidCall(instance).AndReturn('foo')
+ self.mox.ReplayAll()
+
+ # This should fail, since the instances are different
+ self.assertRaises(mox.UnexpectedMethodCallError,
+ TestClass.OtherValidCall, "wrong self")
+
+
+ self.mox.VerifyAll()
+ self.mox.UnsetStubs()
+
+ def testStubOutMethod_CalledAsBoundMethod(self):
+ t = self.mox.CreateMock(TestClass)
+
+ t.MethodWithArgs(mox.IgnoreArg(), mox.IgnoreArg()).AndReturn('foo')
+ self.mox.ReplayAll()
+
+ actual = t.MethodWithArgs(None, None);
+
+ self.mox.VerifyAll()
+ self.mox.UnsetStubs()
+ self.assertEquals('foo', actual)
+
def testStubOutClass_OldStyle(self):
"""Test a mocked class whose __init__ returns a Mock."""
self.mox.StubOutWithMock(mox_test_helper, 'TestClassFromAnotherModule')