diff options
Diffstat (limited to 'mox.py')
-rwxr-xr-x | mox.py | 106 |
1 files changed, 87 insertions, 19 deletions
@@ -224,6 +224,30 @@ class UnexpectedMockCreationError(Error): return error +class SwallowedExceptionError(Error): + """Raised when Verify() is called after something already threw an exception. + + This means that the exception that was thrown was somehow swallowed, allowing + the test to continue when it should not have. + """ + + def __init__(self, previous_exceptions): + """Init exception. + + Args: + # previous_exceptions: A sequence of Error objects that were raised. + previous_exceptions: [Error] + """ + + Error.__init__(self) + self._previous_exceptions = previous_exceptions + + def __str__(self): + exceptions = "\n".join(["%3d. %s: %s" % (i, e.__class__.__name__, e) + for i, e in enumerate(self._previous_exceptions)]) + return "Previous exceptions thrown:\n%s" % (exceptions,) + + class Mox(object): """Mox: a factory for creating mock objects.""" @@ -426,8 +450,18 @@ class MockAnything: created, for debugging output purposes. """ self._description = description + self._exceptions_thrown = [] self._Reset() + def __bases__(self): + pass + + def __members__(self): + pass + + def __methods__(self): + pass + def __repr__(self): if self._description: return '<MockAnything instance of %s>' % self._description @@ -467,7 +501,8 @@ class MockAnything: """ return MockMethod(method_name, self._expected_calls_queue, - self._replay_mode, method_to_mock=method_to_mock, + self._exceptions_thrown, self._replay_mode, + method_to_mock=method_to_mock, description=self._description) def __nonzero__(self): @@ -498,8 +533,15 @@ class MockAnything: Raises: ExpectedMethodCallsError: if there are still more method calls in the expected queue. + any exception previously raised by this object: if _Verify was called + afterwards anyway. (This detects tests passing erroneously.) """ + # If any exceptions were thrown, re-raise them. (This should only happen + # if the original exception was swallowed, in which case it's necessary to + # re-raise it so that the test will fail. See Issue #16.) + if self._exceptions_thrown: + raise SwallowedExceptionError(self._exceptions_thrown) # If the list of expected calls is not empty, raise an exception if self._expected_calls_queue: # The last MultipleTimesGroup is not popped from the queue. @@ -610,7 +652,9 @@ class MockObject(MockAnything, object): name, method_to_mock=getattr(self._class_to_mock, name)) - raise UnknownMethodCallError(name) + exception = UnknownMethodCallError(name) + self._exceptions_thrown.append(exception) + raise exception def __eq__(self, rhs): """Provide custom logic to compare objects.""" @@ -644,7 +688,7 @@ class MockObject(MockAnything, object): # If we are in replay mode then simply call the mock __setitem__ method. if self._replay_mode: return MockMethod('__setitem__', self._expected_calls_queue, - self._replay_mode)(key, value) + self._exceptions_thrown, self._replay_mode)(key, value) # Otherwise, create a mock method __setitem__. @@ -673,7 +717,7 @@ class MockObject(MockAnything, object): # If we are in replay mode then simply call the mock __getitem__ method. if self._replay_mode: return MockMethod('__getitem__', self._expected_calls_queue, - self._replay_mode)(key) + self._exceptions_thrown, self._replay_mode)(key) # Otherwise, create a mock method __getitem__. @@ -713,7 +757,7 @@ class MockObject(MockAnything, object): # If we are in replay mode then simply call the mock __iter__ method. if self._replay_mode: return MockMethod('__iter__', self._expected_calls_queue, - self._replay_mode)() + self._exceptions_thrown, self._replay_mode)() # Otherwise, create a mock method __iter__. @@ -743,7 +787,7 @@ class MockObject(MockAnything, object): if self._replay_mode: return MockMethod('__contains__', self._expected_calls_queue, - self._replay_mode)(key) + self._exceptions_thrown, self._replay_mode)(key) return self._CreateMockMethod('__contains__')(key) @@ -809,8 +853,10 @@ class _MockObjectFactory(MockObject): if self._replay_mode: if not self._instance_queue: - raise UnexpectedMockCreationError(self._class_to_mock, *params, - **named_params) + exception = UnexpectedMockCreationError(self._class_to_mock, *params, + **named_params) + self._exceptions_thrown.append(exception) + raise exception mock_method(*params, **named_params) @@ -967,7 +1013,7 @@ class MockMethod(object): signature) matches the expected method. """ - def __init__(self, method_name, call_queue, replay_mode, + def __init__(self, method_name, call_queue, exception_list, replay_mode, method_to_mock=None, description=None): """Construct a new mock method. @@ -975,6 +1021,8 @@ class MockMethod(object): # method_name: the name of the method # call_queue: deque of calls, verify this call against the head, or add # this call to the queue. + # exception_list: list of exceptions; any exceptions thrown by this + # instance are appended to this list. # replay_mode: False if we are recording, True if we are verifying calls # against the call queue. # method_to_mock: The actual method being mocked, used for introspection. @@ -982,6 +1030,7 @@ class MockMethod(object): # this is equal to the descriptive name of the method's class. method_name: str call_queue: list or deque + exception_list: list replay_mode: bool method_to_mock: a method object description: str or None @@ -992,6 +1041,7 @@ class MockMethod(object): self._call_queue = call_queue if not isinstance(call_queue, deque): self._call_queue = deque(self._call_queue) + self._exception_list = exception_list self._replay_mode = replay_mode self._description = description @@ -1061,7 +1111,9 @@ class MockMethod(object): try: return self._call_queue.popleft() except IndexError: - raise UnexpectedMethodCallError(self, None) + exception = UnexpectedMethodCallError(self, None) + self._exception_list.append(exception) + raise exception def _VerifyMethodCall(self): """Verify the called method is expected. @@ -1086,7 +1138,9 @@ class MockMethod(object): # This is a mock method, so just check equality. if expected != self: - raise UnexpectedMethodCallError(self, expected) + exception = UnexpectedMethodCallError(self, expected) + self._exception_list.append(exception) + raise exception return expected @@ -1158,7 +1212,7 @@ class MockMethod(object): return self # Create a new group and add the method. - new_group = group_class(group_name) + new_group = group_class(group_name, self._exception_list) new_group.AddMethod(self) self._call_queue.append(new_group) return self @@ -1831,8 +1885,18 @@ class Remember(Comparator): class MethodGroup(object): """Base class containing common behaviour for MethodGroups.""" - def __init__(self, group_name): + def __init__(self, group_name, exception_list): + """Construct a new method group. + + Args: + # group_name: the name of the method group + # exception_list: list of exceptions; any exceptions thrown by this + # instance are appended to this list. + group_name: str + exception_list: list + """ self._group_name = group_name + self._exception_list = exception_list def group_name(self): return self._group_name @@ -1856,8 +1920,8 @@ class UnorderedGroup(MethodGroup): over the keys of a dict. """ - def __init__(self, group_name): - super(UnorderedGroup, self).__init__(group_name) + def __init__(self, group_name, exception_list): + super(UnorderedGroup, self).__init__(group_name, exception_list) self._methods = [] def __str__(self): @@ -1907,7 +1971,9 @@ class UnorderedGroup(MethodGroup): return self, method - raise UnexpectedMethodCallError(mock_method, self) + exception = UnexpectedMethodCallError(mock_method, self) + self._exception_list.append(exception) + raise exception def IsSatisfied(self): """Return True if there are not any methods in this group.""" @@ -1923,8 +1989,8 @@ class MultipleTimesGroup(MethodGroup): This is helpful, if you don't know or care how many times a method is called. """ - def __init__(self, group_name): - super(MultipleTimesGroup, self).__init__(group_name) + def __init__(self, group_name, exception_list): + super(MultipleTimesGroup, self).__init__(group_name, exception_list) self._methods = set() self._methods_left = set() @@ -1968,7 +2034,9 @@ class MultipleTimesGroup(MethodGroup): next_method = mock_method._PopNextMethod(); return next_method, None else: - raise UnexpectedMethodCallError(mock_method, self) + exception = UnexpectedMethodCallError(mock_method, self) + self._exception_list.append(exception) + raise exception def IsSatisfied(self): """Return True if all methods in this group are called at least once.""" |