diff options
-rw-r--r-- | NEWS | 5 | ||||
-rw-r--r-- | mocker.py | 63 | ||||
-rwxr-xr-x | test.py | 96 |
3 files changed, 101 insertions, 63 deletions
@@ -1,6 +1,11 @@ 0.9.3 (2007-11-XX) ================== +- Added support for Python 2.3 (patch by Phillip J. Eby). + +- Added MockerTestCase.assert{True,False} aliases, so that they're + available even in Python 2.3. + - Introduced automatic test coverage verification as part of the test suite, based on the 'coverage' module by Ned Batchelder, to ensure that it continues to have 100% of statement coverage. @@ -14,6 +14,10 @@ import os import gc +# That's a single line to prevent breaking coverage tests in 2.4+. +if sys.version_info < (2, 4): from sets import Set as set + + __all__ = ["Mocker", "expect", "IS", "CONTAINS", "IN", "ANY", "ARGS", "KWARGS"] @@ -261,6 +265,10 @@ class MockerTestCase(unittest.TestCase): assertNotApproximates = failIfApproximates assertMethodsMatch = failUnlessMethodsMatch + # The following are missing in Python < 2.4. + assertTrue = unittest.TestCase.failUnless + assertFalse = unittest.TestCase.failIf + # The following is provided for compatibility with Twisted's trial. assertIdentical = assertIs assertNotIdentical = assertIsNot @@ -450,7 +458,7 @@ class MockerBase(object): for error in errors: lines = error.splitlines() message.append("=> " + lines.pop(0)) - message.extend(" " + line for line in lines) + message.extend([" " + line for line in lines]) message.append("") raise AssertionError(os.linesep.join(message)) @@ -636,12 +644,17 @@ class MockerBase(object): recorder(self, event) return Mock(self, path) else: - for event in sorted(self._events, key=lambda e:e.satisfied()): + satisfied = [] + for event in self._events: + if event.satisfied(): + satisfied.append(event) + elif event.matches(path): + return event.run(path) + for event in satisfied: if event.matches(path): return event.run(path) raise MatchError(ERROR_PREFIX + "Unexpected expression: %s" % path) - @classinstancemethod def get_recorders(cls, self): """Return recorders associated with this mocker class or instance. @@ -649,8 +662,8 @@ class MockerBase(object): classes. See the L{add_recorder()} method for more information. """ return (self or cls)._recorders[:] + get_recorders = classinstancemethod(get_recorders) - @classinstancemethod def add_recorder(cls, self, recorder): """Add a recorder to this mocker class or instance. @@ -669,8 +682,8 @@ class MockerBase(object): """ (self or cls)._recorders.append(recorder) return recorder + add_recorder = classinstancemethod(add_recorder) - @classinstancemethod def remove_recorder(cls, self, recorder): """Remove the given recorder from this mocker class or instance. @@ -678,6 +691,7 @@ class MockerBase(object): instances. See the L{add_recorder()} method for more information. """ (self or cls)._recorders.remove(recorder) + remove_recorder = classinstancemethod(remove_recorder) def result(self, value): """Make the last recorded event return the given value on replay. @@ -978,7 +992,7 @@ class Mock(object): lines = str(e).splitlines() message = [ERROR_PREFIX + "Unmet expectation:", ""] message.append("=> " + lines.pop(0)) - message.extend(" " + line for line in lines) + message.extend([" " + line for line in lines]) message.append("") raise AssertionError(os.linesep.join(message)) @@ -1148,12 +1162,12 @@ class Path(object): self.actions = tuple(actions) self.__mocker_replace__ = False - @property def parent_path(self): if not self.actions: return None return self.actions[-1].path - + parent_path = property(parent_path) + def __add__(self, action): """Return a new path which includes the given action at the end.""" return self.__class__(self.root_mock, self.root_object, @@ -1207,7 +1221,9 @@ class Path(object): result = "del %s.%s" % (result, action.args[0]) elif action.kind == "call": args = [repr(x) for x in action.args] - for pair in sorted(action.kwargs.iteritems()): + items = list(action.kwargs.iteritems()) + items.sort() + for pair in items: args.append("%s=%r" % pair) result = "%s(%s)" % (result, ", ".join(args)) elif action.kind == "contains": @@ -1440,7 +1456,7 @@ class Event(object): for error in errors: lines = error.splitlines() message.append("- " + lines.pop(0)) - message.extend(" " + line for line in lines) + message.extend([" " + line for line in lines]) raise AssertionError(os.linesep.join(message)) return result @@ -1478,7 +1494,7 @@ class Event(object): for error in errors: lines = error.splitlines() message.append("- " + lines.pop(0)) - message.extend(" " + line for line in lines) + message.extend([" " + line for line in lines]) raise AssertionError(os.linesep.join(message)) def replay(self): @@ -1560,10 +1576,11 @@ class PathMatcher(Task): def matches(self, path): return self.path.matches(path) -@recorder def path_matcher_recorder(mocker, event): event.add_task(PathMatcher(event.path)) +Mocker.add_recorder(path_matcher_recorder) + class RunCounter(Task): """Task which verifies if the number of runs are within given boundaries. @@ -1601,27 +1618,29 @@ class ImplicitRunCounter(RunCounter): implicit ones. """ -@recorder def run_counter_recorder(mocker, event): """Any event may be repeated once, unless disabled by default.""" if event.path.root_mock.__mocker_count__: event.add_task(ImplicitRunCounter(1)) -@recorder +Mocker.add_recorder(run_counter_recorder) + def run_counter_removal_recorder(mocker, event): """ Events created by getattr actions which lead to other events may be repeated any number of times. For that, we remove implicit - run counters of any getattr actions leading to current one. + run counters of any getattr actions leading to the current one. """ parent_path = event.path.parent_path - for event in reversed(mocker.get_events()): + for event in mocker.get_events()[::-1]: if (event.path is parent_path and event.path.actions[-1].kind == "getattr"): for task in event.get_tasks(): if type(task) is ImplicitRunCounter: event.remove_task(task) +Mocker.add_recorder(run_counter_removal_recorder) + class MockReturner(Task): """Return a mock based on the action path.""" @@ -1632,7 +1651,6 @@ class MockReturner(Task): def run(self, path): return Mock(self.mocker, path) -@recorder def mock_returner_recorder(mocker, event): """Events that lead to other events must return mock objects.""" parent_path = event.path.parent_path @@ -1645,6 +1663,8 @@ def mock_returner_recorder(mocker, event): event.add_task(MockReturner(mocker)) break +Mocker.add_recorder(mock_returner_recorder) + class FunctionRunner(Task): """Task that runs a function everything it's run. @@ -1760,7 +1780,6 @@ class SpecChecker(Task): if obtained_kwargs and not self._varkwargs: self._raise("unknown kwargs: %s" % ", ".join(obtained_kwargs)) -@recorder def spec_checker_recorder(mocker, event): spec = event.path.root_mock.__mocker_spec__ if spec: @@ -1774,6 +1793,8 @@ def spec_checker_recorder(mocker, event): method = getattr(spec, actions[0].args[0], None) event.add_task(SpecChecker(method)) +Mocker.add_recorder(spec_checker_recorder) + class ProxyReplacer(Task): """Task which installs and deinstalls proxy mocks. @@ -1826,7 +1847,7 @@ class Patcher(Task): cls = type(obj) if issubclass(cls, type): cls = obj - bases = set(id(base) for base in cls.__mro__) + bases = set([id(base) for base in cls.__mro__]) bases.intersection_update(monitored) return bool(bases) return False @@ -1915,10 +1936,10 @@ class PatchedMethod(object): return mock.__mocker_act__(self._kind, args, kwargs, object) return method - -@recorder def patcher_recorder(mocker, event): mock = event.path.root_mock if mock.__mocker_patcher__ and len(event.path.actions) == 1: patcher = mock.__mocker_patcher__ patcher.monitor(mock.__mocker_object__, event.path.actions[0].kind) + +Mocker.add_recorder(patcher_recorder) @@ -18,21 +18,28 @@ else: coverage.erase() coverage.start() -from mocker import ( - MockerBase, Mocker, Mock, Event, Task, Action, Path, recorder, expect, - PathMatcher, path_matcher_recorder, RunCounter, ImplicitRunCounter, - run_counter_recorder, run_counter_removal_recorder, MockReturner, - mock_returner_recorder, FunctionRunner, Orderer, SpecChecker, - spec_checker_recorder, match_params, ANY, IS, CONTAINS, IN, ARGS, KWARGS, - MatchError, PathExecuter, ProxyReplacer, Patcher, Undefined, PatchedMethod, - MockerTestCase, ReplayRestoreEvent, OnRestoreCaller) +from mocker import \ + MockerBase, Mocker, Mock, Event, Task, Action, Path, recorder, expect, \ + PathMatcher, path_matcher_recorder, RunCounter, ImplicitRunCounter, \ + run_counter_recorder, run_counter_removal_recorder, MockReturner, \ + mock_returner_recorder, FunctionRunner, Orderer, SpecChecker, \ + spec_checker_recorder, match_params, ANY, IS, CONTAINS, IN, ARGS, KWARGS, \ + MatchError, PathExecuter, ProxyReplacer, Patcher, Undefined, \ + PatchedMethod, MockerTestCase, ReplayRestoreEvent, OnRestoreCaller + + +class TestCase(unittest.TestCase): + """Python 2.3 lacked a couple of useful aliases.""" + + assertTrue = unittest.TestCase.failUnless + assertFalse = unittest.TestCase.failIf class CleanMocker(MockerBase): """Just a better name for MockerBase in a testing context.""" -class IntegrationTest(unittest.TestCase): +class IntegrationTest(TestCase): def setUp(self): self.mocker = Mocker() @@ -235,7 +242,7 @@ class IntegrationTest(unittest.TestCase): self.assertEquals(mock(), 42) -class ExpectTest(unittest.TestCase): +class ExpectTest(TestCase): def setUp(self): self.mocker = CleanMocker() @@ -253,7 +260,7 @@ class ExpectTest(unittest.TestCase): self.assertEquals(obj.attr, 42) -class MockerTestCaseTest(unittest.TestCase): +class MockerTestCaseTest(TestCase): def setUp(self): self.test = MockerTestCase("shortDescription") @@ -270,7 +277,7 @@ class MockerTestCaseTest(unittest.TestCase): self.assertTrue(self.test.expect is expect) def test_constructor_is_the_same(self): - self.assertEquals(inspect.getargspec(unittest.TestCase.__init__), + self.assertEquals(inspect.getargspec(TestCase.__init__), inspect.getargspec(MockerTestCase.__init__)) def test_docstring_is_the_same(self): @@ -284,7 +291,7 @@ class MockerTestCaseTest(unittest.TestCase): class MyTest(MockerTestCase): def test_method(self): """Hello there!""" - class StandardTest(unittest.TestCase): + class StandardTest(TestCase): def test_method(self): """Hello there!""" @@ -292,7 +299,7 @@ class MockerTestCaseTest(unittest.TestCase): StandardTest("test_method").shortDescription()) def test_missing_method_raises_the_same_error(self): - class MyTest(unittest.TestCase): + class MyTest(TestCase): pass try: @@ -708,6 +715,13 @@ class MockerTestCaseTest(unittest.TestCase): self.assertEquals(get_method("failIfIdentical"), get_method("failIfIs")) + def test_missing_python23_aliases(self): + self.assertEquals(MockerTestCase.assertTrue.im_func, + MockerTestCase.failUnless.im_func) + + self.assertEquals(MockerTestCase.assertFalse.im_func, + MockerTestCase.failIf.im_func) + def test_make_file_returns_filename(self): filename = self.test.makeFile() self.assertEquals(os.path.getsize(filename), 0) @@ -796,14 +810,14 @@ class MockerTestCaseTest(unittest.TestCase): shutil.rmtree(parent_dirname) -class MockerTest(unittest.TestCase): +class MockerTest(TestCase): def setUp(self): self.recorded = [] self.mocker = CleanMocker() - @self.mocker.add_recorder def recorder(mocker, event): self.recorded.append((mocker, event)) + self.mocker.add_recorder(recorder) self.action = Action("getattr", ("attr",), {}, Path(Mock(self.mocker, name="mock"))) @@ -1533,7 +1547,7 @@ class MockerTest(unittest.TestCase): self.assertEquals(mock.__mocker_spec__, None) -class ActionTest(unittest.TestCase): +class ActionTest(TestCase): def setUp(self): self.mock = Mock(None, name="mock") @@ -1586,7 +1600,7 @@ class ActionTest(unittest.TestCase): self.assertEquals(action.execute(obj), 3) def test_execute_contains(self): - obj = set(["a"]) + obj = ["a"] action = Action("contains", ("a",), {}) self.assertEquals(action.execute(obj), True) action = Action("contains", ("b",), {}) @@ -1686,12 +1700,11 @@ class ActionTest(unittest.TestCase): self.assertFalse(action1.matches(action2)) -class PathTest(unittest.TestCase): +class PathTest(TestCase): def setUp(self): class StubMocker(object): - @staticmethod - def act(path): + def act(self, path): pass self.mocker = StubMocker() self.mock = Mock(self.mocker, name="obj") @@ -1919,7 +1932,7 @@ class PathTest(unittest.TestCase): self.assertEquals(path.execute(obj), 3) -class MatchParamsTest(unittest.TestCase): +class MatchParamsTest(TestCase): def true(self, *args): self.assertTrue(match_params(*args), repr(args)) @@ -2088,7 +2101,7 @@ class MatchParamsTest(unittest.TestCase): (1, 2, 4, 5, 6), {}) -class MockTest(unittest.TestCase): +class MockTest(TestCase): def setUp(self): self.paths = [] @@ -2098,8 +2111,7 @@ class MockTest(unittest.TestCase): return self._recording def replay(self): self._recording = False - @staticmethod - def act(path): + def act(_, path): self.paths.append(path) return 42 self.StubMocker = StubMocker @@ -2401,7 +2413,7 @@ class MockTest(unittest.TestCase): -class EventTest(unittest.TestCase): +class EventTest(TestCase): def setUp(self): self.event = Event() @@ -2573,7 +2585,7 @@ class EventTest(unittest.TestCase): self.assertEquals(calls, ["task1", "task2"]) -class ReplayRestoreEventTest(unittest.TestCase): +class ReplayRestoreEventTest(TestCase): def setUp(self): self.event = ReplayRestoreEvent() @@ -2584,7 +2596,7 @@ class ReplayRestoreEventTest(unittest.TestCase): self.assertEquals(self.event.matches(None), False) -class TaskTest(unittest.TestCase): +class TaskTest(TestCase): def setUp(self): self.task = Task() @@ -2605,7 +2617,7 @@ class TaskTest(unittest.TestCase): self.assertEquals(self.task.restore(), None) -class OnRestoreCallerTest(unittest.TestCase): +class OnRestoreCallerTest(TestCase): def setUp(self): self.mocker = CleanMocker() @@ -2624,7 +2636,7 @@ class OnRestoreCallerTest(unittest.TestCase): self.assertEquals(calls, ["callback", "callback"]) -class PathMatcherTest(unittest.TestCase): +class PathMatcherTest(TestCase): def setUp(self): self.mocker = CleanMocker() @@ -2658,7 +2670,7 @@ class PathMatcherTest(unittest.TestCase): self.assertTrue(path_matcher_recorder in Mocker.get_recorders()) -class RunCounterTest(unittest.TestCase): +class RunCounterTest(TestCase): def setUp(self): self.mocker = CleanMocker() @@ -2829,7 +2841,7 @@ class RunCounterTest(unittest.TestCase): self.assertTrue(run_counter_removal_recorder in Mocker.get_recorders()) -class MockReturnerTest(unittest.TestCase): +class MockReturnerTest(TestCase): def setUp(self): self.mocker = CleanMocker() @@ -2886,7 +2898,7 @@ class MockReturnerTest(unittest.TestCase): self.assertTrue(mock_returner_recorder in Mocker.get_recorders()) -class FunctionRunnerTest(unittest.TestCase): +class FunctionRunnerTest(TestCase): def setUp(self): self.mocker = CleanMocker() @@ -2904,7 +2916,7 @@ class FunctionRunnerTest(unittest.TestCase): self.assertEquals(result, "((1, 2), {'c': 3})") -class PathExecuterTest(unittest.TestCase): +class PathExecuterTest(TestCase): def setUp(self): self.mocker = CleanMocker() @@ -2943,7 +2955,7 @@ class PathExecuterTest(unittest.TestCase): self.assertEquals(calls, [42]) -class OrdererTest(unittest.TestCase): +class OrdererTest(TestCase): def setUp(self): self.mocker = CleanMocker() @@ -2992,7 +3004,7 @@ class OrdererTest(unittest.TestCase): self.assertEquals(orderer.get_dependencies(), [1, 2]) -class SpecCheckerTest(unittest.TestCase): +class SpecCheckerTest(TestCase): def setUp(self): class C(object): @@ -3001,15 +3013,15 @@ class SpecCheckerTest(unittest.TestCase): def varargs(self, a, b, c=3, *args): pass def varkwargs(self, a, b, c=3, **kwargs): pass def varargskwargs(self, a, b, c=3, *args, **kwargs): pass - @classmethod def klass(cls, a, b, c=3): pass - @staticmethod + klass = classmethod(klass) def static(a, b, c=3): pass + static = staticmethod(static) def noargs(self): pass - @classmethod def klassnoargs(cls): pass - @staticmethod + klassnoargs = classmethod(klassnoargs) def staticnoargs(): pass + staticnoargs = staticmethod(staticnoargs) self.cls = C self.mocker = CleanMocker() self.mock = self.mocker.mock(self.cls) @@ -3187,7 +3199,7 @@ class SpecCheckerTest(unittest.TestCase): self.bad("unexistent", "") -class ProxyReplacerTest(unittest.TestCase): +class ProxyReplacerTest(TestCase): def setUp(self): self.mocker = CleanMocker() @@ -3292,7 +3304,7 @@ class ProxyReplacerTest(unittest.TestCase): self.assertEquals(type(os.path), ModuleType) -class PatcherTest(unittest.TestCase): +class PatcherTest(TestCase): def setUp(self): self.mocker = Mocker() |