diff -u mox-0.5.3-googlecode//mox.py mox-0.5.3-pypi//mox.py --- mox-0.5.3-googlecode//mox.py 2010-04-30 20:56:05.000000000 +0000 +++ mox-0.5.3-pypi//mox.py 2010-07-10 18:26:30.000000000 +0000 @@ -230,7 +230,9 @@ # A list of types that should be stubbed out with MockObjects (as # opposed to MockAnythings). _USE_MOCK_OBJECT = [types.ClassType, types.FunctionType, types.InstanceType, - types.ModuleType, types.ObjectType, types.TypeType] + types.ModuleType, types.ObjectType, types.TypeType, + types.MethodType, types.UnboundMethodType, + ] # A list of types that may be stubbed out with a MockObjectFactory. _USE_MOCK_FACTORY = [types.ClassType, types.ObjectType, types.TypeType] @@ -423,11 +425,11 @@ self._description = description self._Reset() - def __str__(self): - return "" % id(self) - def __repr__(self): - return '' + if self._description: + return '' % self._description + else: + return '' def __getattr__(self, method_name): """Intercept method calls on this object. @@ -754,7 +756,7 @@ # If we are mocking a Function, then use the function, and not the # __call__ method method = None - if type(self._class_to_mock) == types.FunctionType: + if type(self._class_to_mock) in (types.FunctionType, types.MethodType): method = self._class_to_mock; else: method = getattr(self._class_to_mock, '__call__') @@ -817,7 +819,7 @@ super(_MockObjectFactory, self)._Verify() -class MethodCallChecker(object): +class MethodSignatureChecker(object): """Ensures that methods are called correctly.""" _NEEDED, _DEFAULT, _GIVEN = range(3) @@ -841,6 +843,7 @@ 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 @@ -863,9 +866,9 @@ 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. @@ -879,10 +882,26 @@ 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)): @@ -904,9 +923,9 @@ # 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' + raise AttributeError('No values given for arguments: %s' % (' '.join(sorted(still_needed)))) @@ -954,7 +973,7 @@ self._side_effects = None try: - self._checker = MethodCallChecker(method_to_mock) + self._checker = MethodSignatureChecker(method_to_mock) except ValueError: self._checker = None @@ -1218,6 +1237,17 @@ def __ne__(self, rhs): return not self.equals(rhs) +class Is(Comparator): + """Comparison class used to check identity, instead of equality.""" + + def __init__(self, obj): + self._obj = obj + + def equals(self, rhs): + return rhs is self._obj + + def __repr__(self): + return "" % (self._obj, id(self._obj)) class IsA(Comparator): """This class wraps a basic Python type or class. It is used to verify @@ -1397,7 +1427,7 @@ return self._key in rhs def __repr__(self): - return '' % self._key + return '' % str(self._key) class Not(Comparator): @@ -1465,7 +1495,8 @@ return False def __repr__(self): - return '' % (self._key, self._value) + return '' % (str(self._key), + str(self._value)) class ContainsAttributeValue(Comparator): diff -u mox-0.5.3-googlecode//mox_test_helper.py mox-0.5.3-pypi//mox_test_helper.py --- mox-0.5.3-googlecode//mox_test_helper.py 2010-04-30 20:57:47.000000000 +0000 +++ mox-0.5.3-pypi//mox_test_helper.py 2010-06-17 19:32:46.000000000 +0000 @@ -109,3 +109,7 @@ def MyTestFunction(one, two, nine=None): pass + +class ExampleClass(object): + def TestMethod(self, one, two, nine=None): + pass diff -u mox-0.5.3-googlecode//mox_test.py mox-0.5.3-pypi//mox_test.py --- mox-0.5.3-googlecode//mox_test.py 2010-04-30 20:56:56.000000000 +0000 +++ mox-0.5.3-pypi//mox_test.py 2010-07-10 18:23:50.000000000 +0000 @@ -185,6 +185,19 @@ """Should return True if the item is a key in a dict.""" self.assert_(mox.In("test") == {"test" : "module"}) + def testItemInTuple(self): + """Should return True if the item is in the list.""" + self.assert_(mox.In(1) == (1, 2, 3)) + + def testTupleInTupleOfTuples(self): + self.assert_(mox.In((1, 2, 3)) == ((1, 2, 3), (1, 2))) + + def testItemNotInList(self): + self.failIf(mox.In(1) == [2, 3]) + + def testTupleNotInTupleOfTuples(self): + self.failIf(mox.In((1, 2)) == ((1, 2, 3), (4, 5))) + class NotTest(unittest.TestCase): """Test Not correctly identifies False predicates.""" @@ -259,6 +272,52 @@ "") +class IsTest(unittest.TestCase): + """Verify Is correctly checks equality based upon identity, not value""" + + class AlwaysComparesTrue(object): + def __eq__(self, other): + return True + def __cmp__(self, other): + return 0 + def __ne__(self, other): + return False + + def testEqualityValid(self): + o1 = self.AlwaysComparesTrue() + self.assertTrue(mox.Is(o1), o1) + + def testEqualityInvalid(self): + o1 = self.AlwaysComparesTrue() + o2 = self.AlwaysComparesTrue() + self.assertTrue(o1 == o2) + # but... + self.assertFalse(mox.Is(o1) == o2) + + def testInequalityValid(self): + o1 = self.AlwaysComparesTrue() + o2 = self.AlwaysComparesTrue() + self.assertTrue(mox.Is(o1) != o2) + + def testInequalityInvalid(self): + o1 = self.AlwaysComparesTrue() + self.assertFalse(mox.Is(o1) != o1) + + def testEqualityInListValid(self): + o1 = self.AlwaysComparesTrue() + o2 = self.AlwaysComparesTrue() + isa_list = [mox.Is(o1), mox.Is(o2)] + str_list = [o1, o2] + self.assertTrue(isa_list == str_list) + + def testEquailtyInListInvalid(self): + o1 = self.AlwaysComparesTrue() + o2 = self.AlwaysComparesTrue() + isa_list = [mox.Is(o1), mox.Is(o2)] + mixed_list = [o2, o1] + self.assertFalse(isa_list == mixed_list) + + class IsATest(unittest.TestCase): """Verify IsA correctly checks equality based upon class type, not value.""" @@ -490,6 +549,13 @@ """Calling repr on a MockAnything instance must work.""" self.assertEqual('', repr(self.mock_object)) + def testCanMockStr(self): + self.mock_object.__str__().AndReturn("foo"); + self.mock_object._Replay() + actual = str(self.mock_object) + self.mock_object._Verify(); + self.assertEquals("foo", actual) + def testSetupMode(self): """Verify the mock will accept any call.""" self.mock_object.NonsenseCall() @@ -508,6 +574,13 @@ self.assertRaises(mox.UnexpectedMethodCallError, self.mock_object.OtherValidCall) +# def testReplayWithUnexpectedCell_BadSignatureWithTuple(self): +# self.mock_object.ValidCall(mox.In((1, 2, 3))) +# self.mock_object._Replay() +# self.assertRaises(mox.UnexpectedMethodCallError, +# self.mock_object.ValidCall, 4) + + def testVerifyWithCompleteReplay(self): """Verify should not raise an exception for a valid replay.""" self.mock_object.ValidCall() # setup method call @@ -1471,11 +1544,14 @@ self.mox.VerifyAll() def testStubOutMethod(self): - """Test that a method is replaced with a MockAnything.""" + """Test that a method is replaced with a MockObject.""" test_obj = TestClass() + method_type = type(test_obj.OtherValidCall) # Replace OtherValidCall with a mock. self.mox.StubOutWithMock(test_obj, 'OtherValidCall') - self.assert_(isinstance(test_obj.OtherValidCall, mox.MockAnything)) + self.assertTrue(isinstance(test_obj.OtherValidCall, mox.MockObject)) + self.assertFalse(type(test_obj.OtherValidCall) is method_type) + test_obj.OtherValidCall().AndReturn('foo') self.mox.ReplayAll() @@ -1484,7 +1560,60 @@ self.mox.VerifyAll() self.mox.UnsetStubs() self.assertEquals('foo', actual) - self.failIf(isinstance(test_obj.OtherValidCall, mox.MockAnything)) + 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.""" @@ -1595,6 +1724,30 @@ self.assertRaises(AttributeError, mox_test_helper.MyTestFunction, 1) self.mox.UnsetStubs() + def _testMethodSignatureVerification(self, stubClass): + # If stubClass is true, the test is run against an a stubbed out class, + # else the test is run against a stubbed out instance. + if stubClass: + self.mox.StubOutWithMock(mox_test_helper.ExampleClass, "TestMethod") + obj = mox_test_helper.ExampleClass() + else: + obj = mox_test_helper.ExampleClass() + self.mox.StubOutWithMock(mox_test_helper.ExampleClass, "TestMethod") + self.assertRaises(AttributeError, obj.TestMethod) + self.assertRaises(AttributeError, obj.TestMethod, 1) + self.assertRaises(AttributeError, obj.TestMethod, nine=2) + obj.TestMethod(1, 2) + obj.TestMethod(1, 2, 3) + obj.TestMethod(1, 2, nine=3) + self.assertRaises(AttributeError, obj.TestMethod, 1, 2, 3, 4) + self.mox.UnsetStubs() + + def testStubOutClassMethodVerifiesSignature(self): + self._testMethodSignatureVerification(stubClass=True) + + def testStubOutObjectMethodVerifiesSignature(self): + self._testMethodSignatureVerification(stubClass=False) + def testStubOutObject(self): """Test than object is replaced with a Mock."""