summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGustavo Niemeyer <gustavo@niemeyer.net>2007-10-30 23:53:06 -0400
committerGustavo Niemeyer <gustavo@niemeyer.net>2007-10-30 23:53:06 -0400
commitdb9037f7c5fa1ebd3fae117abc2eb89ee431c01b (patch)
tree8d2e5f32e4f71651e55e76cbc51115897a7da786
parentab0e17829e138bd087faf03c58f778a72a9bda4c (diff)
downloadmocker-db9037f7c5fa1ebd3fae117abc2eb89ee431c01b.tar.gz
- Now if replay() is run while already in replay mode, the mocker
is restored and then put back in replay mode - Implemented Mocker.reset(). - Now patch() sets the spec to the given object by default, and the new spec argument acts like usual. - Got rid of ordered(), and integrated its functionality in order() itself, when called without arguments. unordered() was renamed to unorder() for symmetry. - passthrough() now takes a result_callback argument, called with the result of the real function as the only argument. - Added setattr events in the mock.
-rw-r--r--mocker.py230
-rwxr-xr-xtest.py189
2 files changed, 292 insertions, 127 deletions
diff --git a/mocker.py b/mocker.py
index 7da2436..6e88b7c 100644
--- a/mocker.py
+++ b/mocker.py
@@ -149,8 +149,11 @@ class MockerBase(object):
return self._recording
def replay(self):
- """Change state to expect recorded events to be reproduced.
-
+ """Change to replay mode, where recorded events are reproduced.
+
+ If already in replay mode, the mocker will be restored, with all
+ expectations reset, and then put again in replay mode.
+
An alternative and more comfortable way to replay changes is
using the 'with' statement, as follows::
@@ -167,61 +170,37 @@ class MockerBase(object):
Also check the MockerTestCase class, which integrates the
unittest.TestCase class with mocker.
"""
- if self._recording:
- self._recording = False
+ if not self._recording:
for event in self._events:
- event.replay()
+ event.restore()
+ else:
+ self._recording = False
+ for event in self._events:
+ event.replay()
def restore(self):
"""Restore changes in the environment, and return to recording mode.
This should always be called after the test is complete (succeeding
or not). There are ways to call this method automatically on
- completion (e.g. using a with mocker: statement, or using the
- MockerTestCase class.
+ completion (e.g. using a C{with mocker:} statement, or using the
+ L{MockerTestCase} class.
"""
if not self._recording:
self._recording = True
for event in self._events:
event.restore()
- def is_ordering(self):
- """Return true if all events are being ordered.
+ def reset(self):
+ """Reset the mocker state.
- See the L{ordered()} method.
+ This will restore environment changes, if currently in replay
+ mode, and then remove all events previously recorded.
"""
- return self._ordering
-
- def ordered(self):
- """Expect following events to be reproduced in the recorded order.
-
- By default, mocker won't force events to happen precisely in
- the order they were recorded. Calling this method will change
- this behavior so that events will only match if reproduced in
- the correct order.
-
- Running the L{unordered()} method will put the mocker back on
- unordered mode.
-
- This method may also be used with the 'with' statement, like so::
-
- with mocker.ordered():
- <record events>
-
- In this case, only expressions in <record events> will be ordered,
- and the mocker will be back in unordered mode after the 'with' block.
- """
- self._ordering = True
- return OrderedContext(self)
-
- def unordered(self):
- """Don't expect following events to be reproduce in the recorded order.
-
- This will undo the effect of the L{ordered()} method, putting the
- mocker back in the default unordered mode.
- """
- self._ordering = False
- self._last_orderer = None
+ if not self._recording:
+ self.restore()
+ self.unorder()
+ del self._events[:]
def get_events(self):
"""Return all recorded events."""
@@ -371,10 +350,17 @@ class MockerBase(object):
event.add_task(ProxyReplacer(mock))
return mock
- def patch(self, object):
+ def patch(self, object, spec=True):
"""Patch an existing object to reproduce recorded events.
@param object: Class or instance to be patched.
+ @param spec: Method calls will be checked for correctness against
+ the given object, which may be a class or an instance
+ where attributes will be looked up. Defaults to the
+ the C{object} parameter. May be set to None explicitly,
+ in which case spec checking is disabled. Checks may
+ also be disabled explicitly on a per-event basis with
+ the L{nospec()} method.
The result of this method is still a mock object, which can be
used like any other mock object to record events. The difference
@@ -402,10 +388,13 @@ class MockerBase(object):
(explicitly, or implicitly with alternative conventions, such as
a C{with mocker:} block, or a MockerTestCase class).
"""
+ if spec is True:
+ spec = object
patcher = Patcher()
event = self.add_event(Event())
event.add_task(patcher)
- mock = Mock(self, object=object, patcher=patcher, passthrough=True)
+ mock = Mock(self, object=object, patcher=patcher,
+ passthrough=True, spec=spec)
object.__mocker_mock__ = mock
return mock
@@ -505,10 +494,48 @@ class MockerBase(object):
event.remove_task(task)
event.add_task(RunCounter(min, max))
+ def is_ordering(self):
+ """Return true if all events are being ordered.
+
+ See the L{order()} method.
+ """
+ return self._ordering
+
+ def unorder(self):
+ """Disable the ordered mode.
+
+ See the L{order()} method for more information.
+ """
+ self._ordering = False
+ self._last_orderer = None
+
def order(self, *path_holders):
- """Ensure that events referred to by given objects happen in order.
+ """Create an expectation of order between two or more events.
- As an example::
+ @param path_holders: Objects returned as the result of recorded events.
+
+ By default, mocker won't force events to happen precisely in
+ the order they were recorded. Calling this method will change
+ this behavior so that events will only match if reproduced in
+ the correct order.
+
+ There are two ways in which this method may be used. Which one
+ is used in a given occasion depends only on convenience.
+
+ If no arguments are passed, the mocker will be put in a mode where
+ all the recorded events following the method call will only be met
+ if they happen in order. When that's used, the mocker may be put
+ back in unordered mode by calling the L{unorder()} method, or by
+ using a 'with' block, like so::
+
+ with mocker.ordered():
+ <record events>
+
+ In this case, only expressions in <record events> will be ordered,
+ and the mocker will be back in unordered mode after the 'with' block.
+
+ The second way to use it is by specifying precisely which events
+ should be ordered. As an example::
mock = mocker.mock()
expr1 = mock.hello()
@@ -517,11 +544,15 @@ class MockerBase(object):
mocker.order(expr1, expr2, expr3)
This method of ordering only works when the expression returns
- another object. For other methods of ordering check the
- L{ordered()}, L{after()}, and L{before()} methods.
+ another object.
- @param path_holders: Objects returned as the result of recorded events.
+ Also check the L{after()} and L{before()} methods, which are
+ alternative ways to perform this.
"""
+ if not path_holders:
+ self._ordering = True
+ return OrderedContext(self)
+
last_orderer = None
for path_holder in path_holders:
if type(path_holder) is Path:
@@ -545,7 +576,20 @@ class MockerBase(object):
def after(self, *path_holders):
"""Last recorded event must happen after events referred to.
- @param path_holders: Objects returned as the result of recorded events.
+ @param path_holders: Objects returned as the result of recorded events
+ which should happen before the last recorded event
+
+ As an example, the idiom::
+
+ expect(mock.x).after(mock.y, mock.z)
+
+ is an alternative way to say::
+
+ expr_x = mock.x
+ expr_y = mock.y
+ expr_z = mock.z
+ mocker.order(expr_y, expr_x)
+ mocker.order(expr_z, expr_x)
See L{order()} for more information.
"""
@@ -556,7 +600,20 @@ class MockerBase(object):
def before(self, *path_holders):
"""Last recorded event must happen before events referred to.
- @param path_holders: Objects returned as the result of recorded events.
+ @param path_holders: Objects returned as the result of recorded events
+ which should happen after the last recorded event
+
+ As an example, the idiom::
+
+ expect(mock.x).before(mock.y, mock.z)
+
+ is an alternative way to say::
+
+ expr_x = mock.x
+ expr_y = mock.y
+ expr_z = mock.z
+ mocker.order(expr_x, expr_y)
+ mocker.order(expr_x, expr_z)
See L{order()} for more information.
"""
@@ -565,22 +622,36 @@ class MockerBase(object):
self.order(last_path, path_holder)
def nospec(self):
- """Don't check method specification of real object on last event."""
+ """Don't check method specification of real object on last event.
+
+ By default, when using a mock created as the result of a call to
+ L{proxy()}, L{replace()}, and C{patch()}, or when passing the spec
+ attribute to the L{mock()} method, method calls on the given object
+ are checked for correctness against the specification of the real
+ object (or the explicitly provided spec).
+
+ This method will disable that check specifically for the last
+ recorded event.
+ """
event = self._events[-1]
for task in event.get_tasks():
if isinstance(task, SpecChecker):
event.remove_task(task)
- def passthrough(self):
- """Make the last recorder event act on the real object once seen.
+ def passthrough(self, result_callback=None):
+ """Make the last recorded event run on the real object once seen.
+
+ @param result_callback: If given, this function will be called with
+ the result of the *real* method call as the only argument.
This can only be used on proxies, as returned by the L{proxy()}
- and L{replace()} methods, or on patched objects (L{patch()}).
+ and L{replace()} methods, or on mocks representing patched objects,
+ as returned by the L{patch()} method.
"""
event = self._events[-1]
if event.path.root_object is None:
raise TypeError("Mock object isn't a proxy")
- event.add_task(PathExecuter())
+ event.add_task(PathExecuter(result_callback))
def __enter__(self):
"""Enter in a 'with' context. This will run replay()."""
@@ -609,7 +680,7 @@ class OrderedContext(object):
return None
def __exit__(self, type, value, traceback):
- self._mocker.unordered()
+ self._mocker.unorder()
class Mocker(MockerBase):
@@ -669,6 +740,11 @@ class Mock(object):
return super(Mock, self).__getattribute__(name)
return self.__mocker_act__("getattr", (name,))
+ def __setattr__(self, name, value):
+ if name.startswith("__mocker_"):
+ return super(Mock, self).__setattr__(name, value)
+ return self.__mocker_act__("setattr", (name, value))
+
def __call__(self, *args, **kwargs):
return self.__mocker_act__("call", args, kwargs)
@@ -1159,15 +1235,9 @@ class RunCounter(Task):
def verify(self):
if not self.min <= self._runs <= self.max:
- if self.max == sys.maxint:
- raise AssertionError("Expected at least %d time(s), "
- "seen %d time(s)."
- % (self.min, self._runs))
- if self.min == self.max:
- raise AssertionError("Expected %d time(s), seen %d time(s)."
- % (self.min, self._runs))
- raise AssertionError("Expected %d to %d time(s), seen %d time(s)."
- % (self.min, self.max, self._runs))
+ if self._runs < self.min:
+ raise AssertionError("Performed less times than expected.")
+ raise AssertionError("Performed more times than expected.")
class ImplicitRunCounter(RunCounter):
"""RunCounter inserted by default on any event.
@@ -1238,8 +1308,17 @@ class FunctionRunner(Task):
class PathExecuter(Task):
"""Task that executes a path in the real object, and returns the result."""
+ def __init__(self, result_callback=None):
+ self._result_callback = result_callback
+
+ def get_result_callback(self):
+ return self._result_callback
+
def run(self, path):
- return path.execute(path.root_object)
+ result = path.execute(path.root_object)
+ if self._result_callback is not None:
+ self._result_callback(result)
+ return result
class Orderer(Task):
@@ -1316,12 +1395,17 @@ class SpecChecker(Task):
@recorder
def spec_checker_recorder(mocker, event):
- cls = event.path.root_mock.__mocker_spec__
- actions = event.path.actions
- if (cls and len(actions) == 2 and
- actions[0].kind == "getattr" and actions[1].kind == "call"):
- method = getattr(cls, actions[0].args[0], None)
- event.add_task(SpecChecker(method))
+ spec = event.path.root_mock.__mocker_spec__
+ if spec:
+ actions = event.path.actions
+ if len(actions) == 1:
+ if actions[0].kind == "call":
+ method = getattr(spec, "__call__", None)
+ event.add_task(SpecChecker(method))
+ elif len(actions) == 2:
+ if actions[0].kind == "getattr" and actions[1].kind == "call":
+ method = getattr(spec, actions[0].args[0], None)
+ event.add_task(SpecChecker(method))
class ProxyReplacer(Task):
diff --git a/test.py b/test.py
index a0b5bc8..dc7a73c 100755
--- a/test.py
+++ b/test.py
@@ -25,6 +25,9 @@ class IntegrationTest(unittest.TestCase):
def setUp(self):
self.mocker = Mocker()
+ def tearDown(self):
+ self.mocker.restore()
+
def test_count(self):
obj = self.mocker.mock()
obj.x
@@ -39,10 +42,10 @@ class IntegrationTest(unittest.TestCase):
self.mocker.verify()
self.assertRaises(AssertionError, getattr, obj, "x")
- def test_ordered(self):
+ def test_order(self):
obj = self.mocker.mock()
- with_manager = self.mocker.ordered()
+ with_manager = self.mocker.order()
with_manager.__enter__()
obj.x
obj.y
@@ -145,43 +148,46 @@ class IntegrationTest(unittest.TestCase):
self.assertRaises(AssertionError, obj.sum, 0, 0) # Seen twice.
def test_replace_install_and_restore(self):
- try:
- module = self.mocker.replace("calendar")
- import calendar
- self.assertTrue(calendar is not module)
- self.mocker.replay()
- import calendar
- self.assertTrue(calendar is module)
- self.mocker.restore()
- import calendar
- self.assertTrue(calendar is not module)
- finally:
- self.mocker.restore()
+ module = self.mocker.replace("calendar")
+ import calendar
+ self.assertTrue(calendar is not module)
+ self.mocker.replay()
+ import calendar
+ self.assertTrue(calendar is module)
+ self.mocker.restore()
+ import calendar
+ self.assertTrue(calendar is not module)
def test_replace_os_path_join(self):
- try:
- path = self.mocker.replace("os.path")
- expect(path.join(ARGS)).call(lambda *args: "-".join(args))
- expect(path.join("e", ARGS)).passthrough()
- self.mocker.replay()
- import os
- self.assertEquals(os.path.join("a", "b", "c"), "a-b-c")
- self.assertNotEquals(os.path.join("e", "f", "g"), "e-f-g")
- finally:
- self.mocker.restore()
+ path = self.mocker.replace("os.path")
+ expect(path.join(ARGS)).call(lambda *args: "-".join(args))
+ expect(path.join("e", ARGS)).passthrough()
+ self.mocker.replay()
+ import os
+ self.assertEquals(os.path.join("a", "b", "c"), "a-b-c")
+ self.assertNotEquals(os.path.join("e", "f", "g"), "e-f-g")
def test_replace_os_path_isfile(self):
- try:
- path = self.mocker.replace("os.path")
- expect(path.isfile("unexistent")).result(True)
- expect(path.isfile(ANY)).passthrough().count(2)
- self.mocker.replay()
- import os
- self.assertFalse(os.path.isfile("another-unexistent"))
- self.assertTrue(os.path.isfile("unexistent"))
- self.assertFalse(os.path.isfile("unexistent"))
- finally:
- self.mocker.restore()
+ path = self.mocker.replace("os.path")
+ expect(path.isfile("unexistent")).result(True)
+ expect(path.isfile(ANY)).passthrough().count(2)
+ self.mocker.replay()
+ import os
+ self.assertFalse(os.path.isfile("another-unexistent"))
+ self.assertTrue(os.path.isfile("unexistent"))
+ self.assertFalse(os.path.isfile("unexistent"))
+
+ def test_patch_with_spec(self):
+ class C(object):
+ def method(self, a, b):
+ pass
+ mock = self.mocker.patch(C)
+ mock.method(1, 2)
+ mock.method(1)
+ self.mocker.replay()
+ mock.method(1, 2)
+ self.assertRaises(AssertionError, mock.method, 1)
+
class ExpectTest(unittest.TestCase):
@@ -224,9 +230,10 @@ class MockerTest(unittest.TestCase):
task.replay = lambda: calls.append("replay")
task.restore = lambda: calls.append("restore")
self.mocker.replay()
- self.mocker.replay()
self.assertFalse(self.mocker.is_recording())
self.assertEquals(calls, ["replay"])
+ self.mocker.replay()
+ self.assertEquals(calls, ["replay", "restore", "replay"])
def test_restore(self):
calls = []
@@ -240,6 +247,22 @@ class MockerTest(unittest.TestCase):
self.assertTrue(self.mocker.is_recording())
self.assertEquals(calls, ["replay", "restore"])
+ def test_reset(self):
+ calls = []
+ event = self.mocker.add_event(Event())
+ task = event.add_task(Task())
+ task.restore = lambda: calls.append("restore")
+ self.mocker.replay()
+ self.mocker.reset()
+ self.mocker.reset()
+ self.assertEquals(calls, ["restore"])
+ self.assertEquals(self.mocker.get_events(), [])
+
+ def test_reset_removes_ordering(self):
+ self.mocker.order()
+ self.mocker.reset()
+ self.assertFalse(self.mocker.is_ordering())
+
def test_verify(self):
class MyEvent(object):
def __init__(self, id, failed):
@@ -768,21 +791,21 @@ class MockerTest(unittest.TestCase):
def test_default_ordering(self):
self.assertEquals(self.mocker.is_ordering(), False)
- def test_ordered(self):
- self.mocker.ordered()
+ def test_order_without_arguments(self):
+ self.mocker.order()
self.assertEquals(self.mocker.is_ordering(), True)
- def test_ordered_context_manager(self):
- with_manager = self.mocker.ordered()
+ def test_order_with_context_manager(self):
+ with_manager = self.mocker.order()
self.assertEquals(self.mocker.is_ordering(), True)
with_manager.__enter__()
self.assertEquals(self.mocker.is_ordering(), True)
with_manager.__exit__(None, None, None)
self.assertEquals(self.mocker.is_ordering(), False)
- def test_unordered(self):
- self.mocker.ordered()
- self.mocker.unordered()
+ def test_unorder(self):
+ self.mocker.order()
+ self.mocker.unorder()
self.assertEquals(self.mocker.is_ordering(), False)
def test_ordered_events(self):
@@ -790,12 +813,12 @@ class MockerTest(unittest.TestCase):
# Ensure that the state is correctly reset between
# different ordered blocks.
- self.mocker.ordered()
+ self.mocker.order()
mock.a
- self.mocker.unordered()
- self.mocker.ordered()
+ self.mocker.unorder()
+ self.mocker.order()
mock.x.y.z
@@ -840,6 +863,15 @@ class MockerTest(unittest.TestCase):
event2 = self.mocker.add_event(Event(Path(mock)))
self.assertRaises(TypeError, self.mocker.passthrough)
+ def test_passthrough(self):
+ obj = object()
+ mock = self.mocker.proxy(obj)
+ event = self.mocker.add_event(Event(Path(mock, obj)))
+ result_callback = object()
+ self.mocker.passthrough(result_callback)
+ (task,) = event.get_tasks()
+ self.assertEquals(task.get_result_callback(), result_callback)
+
def test_on(self):
obj = self.mocker.mock()
self.mocker.on(obj.attr).result(123)
@@ -854,10 +886,16 @@ class MockerTest(unittest.TestCase):
self.assertTrue(mock.__mocker_object__ is C)
self.assertEquals(type(mock.__mocker_patcher__), Patcher)
self.assertEquals(mock.__mocker_passthrough__, True)
+ self.assertEquals(mock.__mocker_spec__, C)
(event,) = self.mocker.get_events()
(task,) = event.get_tasks()
self.assertTrue(task is mock.__mocker_patcher__)
+ def test_patch_without_spec(self):
+ class C(object): pass
+ mock = self.mocker.patch(C, spec=None)
+ self.assertEquals(mock.__mocker_spec__, None)
+
class ActionTest(unittest.TestCase):
@@ -1389,6 +1427,14 @@ class MockTest(unittest.TestCase):
self.assertEquals(path, self.mock.__mocker_path__ +
Action("getattr", ("attr",), {}))
+ def test_setattr(self):
+ self.mock.attr = 24
+ (path,) = self.paths
+ self.assertEquals(type(path), Path)
+ self.assertTrue(path.parent_path is self.mock.__mocker_path__)
+ self.assertEquals(path, self.mock.__mocker_path__ +
+ Action("setattr", ("attr", 24), {}))
+
def test_call(self):
self.mock(1, a=2)
(path,) = self.paths
@@ -1909,6 +1955,22 @@ class PathExecuterTest(unittest.TestCase):
task = PathExecuter()
self.assertEquals(task.run(path), 3)
+ def test_run_with_result_callback(self):
+ class C(object):
+ def x(self, arg):
+ return 41 + arg
+ obj = C()
+
+ path = Path(None, obj, [Action("getattr", ("x",), {}),
+ Action("call", (1,), {})])
+
+ calls = []
+ result_callback = lambda result: calls.append(result)
+ task = PathExecuter(result_callback)
+ self.assertEquals(task.get_result_callback(), result_callback)
+ self.assertEquals(task.run(path), 42)
+ self.assertEquals(calls, [42])
+
class OrdererTest(unittest.TestCase):
@@ -1947,6 +2009,7 @@ class SpecCheckerTest(unittest.TestCase):
def setUp(self):
class C(object):
+ def __call__(self, a, b, c=3): pass
def normal(self, a, b, c=3): pass
def varargs(self, a, b, c=3, *args): pass
def varkwargs(self, a, b, c=3, **kwargs): pass
@@ -2045,7 +2108,7 @@ class SpecCheckerTest(unittest.TestCase):
def test_recorder_first_action_isnt_getattr(self):
self.mocker.add_recorder(spec_checker_recorder)
obj = self.mocker.mock(spec=self.cls)
- obj("noargs").x
+ obj.__mocker_act__("anyother", ("attr",))()
event1, event2 = self.mocker.get_events()
self.assertEquals(event1.get_tasks(), [])
self.assertEquals(event2.get_tasks(), [])
@@ -2059,6 +2122,23 @@ class SpecCheckerTest(unittest.TestCase):
self.assertEquals(len(event2.get_tasks()), 1)
self.assertEquals(len(event3.get_tasks()), 0)
+ def test_recorder_with_call_on_object(self):
+ self.mocker.add_recorder(spec_checker_recorder)
+ obj = self.mocker.mock(spec=self.cls)
+ obj()
+ (call,) = self.mocker.get_events()
+ (task,) = call.get_tasks()
+ self.assertEquals(type(task), SpecChecker)
+ self.assertEquals(task.get_method(), self.cls.__call__)
+
+ def test_recorder_more_than_one_action_with_direct_call(self):
+ self.mocker.add_recorder(spec_checker_recorder)
+ obj = self.mocker.mock(spec=self.cls)
+ obj().x
+ event1, event2 = self.mocker.get_events()
+ self.assertEquals(len(event1.get_tasks()), 1)
+ self.assertEquals(len(event2.get_tasks()), 0)
+
def test_noargs(self):
methods = ["noargs", "klassnoargs", "staticnoargs"]
self.good(methods, "")
@@ -2066,8 +2146,8 @@ class SpecCheckerTest(unittest.TestCase):
self.bad(methods, "a=1")
def test_args_and_kwargs(self):
- methods = ["normal", "varargs", "varkwargs", "varargskwargs",
- "static", "klass"]
+ methods = ["__call__", "normal", "varargs", "varkwargs",
+ "varargskwargs", "static", "klass"]
self.good(methods, "1, 2")
self.good(methods, "1, 2, 3")
self.good(methods, "1, b=2")
@@ -2076,13 +2156,13 @@ class SpecCheckerTest(unittest.TestCase):
self.good(methods, "a=1, b=2, c=3")
def test_too_much(self):
- methods = ["normal", "static", "klass"]
+ methods = ["__call__", "normal", "static", "klass"]
self.bad(methods, "1, 2, 3, 4")
self.bad(methods, "1, 2, d=4")
def test_missing(self):
- methods = ["normal", "varargs", "varkwargs", "varargskwargs",
- "static", "klass"]
+ methods = ["__call__", "normal", "varargs", "varkwargs",
+ "varargskwargs", "static", "klass"]
self.bad(methods, "")
self.bad(methods, "1")
self.bad(methods, "c=3")
@@ -2090,8 +2170,8 @@ class SpecCheckerTest(unittest.TestCase):
self.bad(methods, "b=2, c=3")
def test_duplicated_argument(self):
- methods = ["normal", "varargs", "varkwargs", "varargskwargs",
- "static", "klass"]
+ methods = ["__call__", "normal", "varargs", "varkwargs",
+ "varargskwargs", "static", "klass"]
self.bad(methods, "1, 2, b=2")
def test_varargs(self):
@@ -2485,6 +2565,7 @@ class PatcherTest(unittest.TestCase):
self.assertEquals(self.patcher.execute(action, obj), ((1,), {"a": 2}))
def test_recorder_class_getattr(self):
+ self.C.method = lambda: None
mock = self.mocker.patch(self.C)
mock.method()
self.mocker.result("mocked")