Index: unittest.py =================================================================== RCS file: /cvsroot/python/python/dist/src/Lib/unittest.py,v retrieving revision 1.22 diff -c -r1.22 unittest.py *** unittest.py 27 Feb 2003 20:14:43 -0000 1.22 --- unittest.py 22 Mar 2003 16:32:53 -0000 *************** *** 324,329 **** --- 324,341 ---- raise self.failureException, \ (msg or '%s == %s within %s places' % (`first`, `second`, `places`)) + def createMockInstance(self, classType, methods=[], *initArgs, + **initKwargs): + """Create a mock instance of specified type (classType) + See MockFactory.py for more detail + """ + self.__callStack = [] + mockObject = MockFactory.__new__(classType, methods, self.__callStack, *initArgs, **initKwargs) + return mockObject + + def getMockInstanceCalls(self): + return self.__callStack + assertEqual = assertEquals = failUnlessEqual assertNotEqual = assertNotEquals = failIfEqual *************** *** 747,752 **** --- 759,882 ---- self.testRunner = TextTestRunner(verbosity=self.verbosity) result = self.testRunner.run(self.test) sys.exit(not result.wasSuccessful()) + + + class MockMethod(object): + """MockMethod(name, realMethod, []) -> mocked instance method + + Provide introspection and manipulation of a method call. + e.g override a method to raise an exception or return + abituary values. + """ + + def __init__(self, name, mockInstance, realMethod, callStack): + """Construct the mock method with a method name, + pass the orignal metod if you desure it to be called + (still retains introspection features) + and a callStack list to track what's been called overall + """ + object.__init__(self) + self.__name__ = name + self.instance = mockInstance + self.realMethod = realMethod + self.callStack = callStack + self.kwargsPassed = [] + self.argsPassed = [] + + def setException(self, exception): + """Set an exception to raise for this method""" + self.eventSequence = [exception] + + def setReturnValue(self, value): + """Set a return value for this method""" + self.eventSequence = [value] + + def setEventSequence(self, seq): + """Set a sequence of events for this instance + Passing an exception in the sequence will cause this instance + to raise that exception for you + """ + self.eventSequence = seq[:] + + def __call__(self, *args, **kwargs): + """Call this instance representing one of your instance's methods + calls your instance's methods also if passed as a non-None value + to this instance's constructor + """ + # Can we check the mock method has a similar interface to the real method? + self.callStack.append(self.__name__) + self.argsPassed.append(args) + self.kwargsPassed.append(kwargs) + + # default to returning None (if u don't do anything) + obj = None + + if self.realMethod is not None: + obj = self.realMethod(*args, **kwargs) + + if hasattr(self, "eventSequence"): + try: + obj = self.eventSequence.pop(0) + except IndexError, ie: + raise ie + else: + if isinstance(obj, Exception): + raise obj + + return obj + + class MockError(Exception): + """\ + Classes implementing __getattr__ and __getattribute__ cannot be mocked. + Consider mocking the objects that those hooks operate on + """ + def __str__(self): + return "%s\n\n%s" % (Exception.__str__(self), self.__doc__) + + class MockFactory(object): + """MockObject Support For TestCase""" + + def __new__(classDef, mockMethods, callStack, *args, **kwargs): + """__new__(classDef, mockMethods, callStack, *initArgsm, **initKwArgs + + classDef - The Class you want to test + mockMethods - Pass a list of methods that you want to override # + Any method passed in this list will return None by default, + leaving you as the tester to override that method to return some abituray + value or raise an exception. + + Passing an empty list will cause all methods in the object to call + the original methods on your classDef, but addtioanlly can still + introspect (getArgsPassed etc) + + callStack - list to hold call information, usually an empty list object + *initArgs & + **initKwargs - Arguments to passed to the constructor of your classDef + """ + self = classDef(*args, **kwargs) + callables = [ name for name in dir(self) if callable(getattr(self, name)) ] + for m in mockMethods: + if not m in callables: + raise AttributeError("Specified method (%s)does not exist" % m) + + for name in dir(self): + attr = getattr(self, name) + if name.startswith("__getattr"): + raise MockError("%s.%s cannot be mocked" % (classDef,name)) + elif name.startswith("__"): + continue + + if callable(attr): + if name in mockMethods: + real = None + else: + real = attr + mockMethod = MockMethod(name, self, real, callStack) + setattr(self, name, mockMethod) + elif attr is None and name not in mockMethods: + raise AttributeError("Cannot mock undefined instance variable (%s)" % name) + return self + main = TestProgram