summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS5
-rw-r--r--mocker.py63
-rwxr-xr-xtest.py96
3 files changed, 101 insertions, 63 deletions
diff --git a/NEWS b/NEWS
index 886c9cd..36c4c55 100644
--- a/NEWS
+++ b/NEWS
@@ -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.
diff --git a/mocker.py b/mocker.py
index 6c9db68..ade8e1b 100644
--- a/mocker.py
+++ b/mocker.py
@@ -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)
diff --git a/test.py b/test.py
index cc88bcd..6d7b774 100755
--- a/test.py
+++ b/test.py
@@ -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()