2
Copyright (c) 2007 Gustavo Niemeyer <gustavo@niemeyer.net>
4
Graceful platform for test doubles in Python (mocks, stubs, fakes, and dummies).
17
if sys.version_info < (2, 4):
18
from sets import Set as set # pragma: nocover
21
__all__ = ["Mocker", "expect", "IS", "CONTAINS", "IN", "MATCH",
22
"ANY", "ARGS", "KWARGS"]
25
__author__ = "Gustavo Niemeyer <gustavo@niemeyer.net>"
26
__license__ = "PSF License"
27
__version__ = "0.10.1"
30
ERROR_PREFIX = "[Mocker] "
33
# --------------------------------------------------------------------
36
class MatchError(AssertionError):
37
"""Raised when an unknown expression is seen in playback mode."""
40
# --------------------------------------------------------------------
41
# Helper for chained-style calling.
44
"""This is a simple helper that allows a different call-style.
46
With this class one can comfortably do chaining of calls to the
47
mocker object responsible by the object being handled. For instance::
49
expect(obj.attr).result(3).count(1, 2)
59
def __init__(self, mock, attr=None):
63
def __getattr__(self, attr):
64
return self.__class__(self._mock, attr)
66
def __call__(self, *args, **kwargs):
67
getattr(self._mock.__mocker__, self._attr)(*args, **kwargs)
71
# --------------------------------------------------------------------
72
# Extensions to Python's unittest.
74
class MockerTestCase(unittest.TestCase):
75
"""unittest.TestCase subclass with Mocker support.
77
@ivar mocker: The mocker instance.
79
This is a convenience only. Mocker may easily be used with the
80
standard C{unittest.TestCase} class if wanted.
82
Test methods have a Mocker instance available on C{self.mocker}.
83
At the end of each test method, expectations of the mocker will
84
be verified, and any requested changes made to the environment
87
In addition to the integration with Mocker, this class provides
88
a few additional helper methods.
93
def __init__(self, methodName="runTest"):
94
# So here is the trick: we take the real test method, wrap it on
95
# a function that do the job we have to do, and insert it in the
96
# *instance* dictionary, so that getattr() will return our
97
# replacement rather than the class method.
98
test_method = getattr(self, methodName, None)
99
if test_method is not None:
100
def test_method_wrapper():
102
result = test_method()
106
if (self.mocker.is_recording() and
107
self.mocker.get_events()):
108
raise RuntimeError("Mocker must be put in replay "
109
"mode with self.mocker.replay()")
110
if (hasattr(result, "addCallback") and
111
hasattr(result, "addErrback")):
115
result.addCallback(verify)
118
self.mocker.restore()
120
# Copy all attributes from the original method..
121
for attr in dir(test_method):
122
# .. unless they're present in our wrapper already.
123
if not hasattr(test_method_wrapper, attr) or attr == "__doc__":
124
setattr(test_method_wrapper, attr,
125
getattr(test_method, attr))
126
setattr(self, methodName, test_method_wrapper)
128
# We could overload run() normally, but other well-known testing
129
# frameworks do it as well, and some of them won't call the super,
130
# which might mean that cleanup wouldn't happen. With that in mind,
131
# we make integration easier by using the following trick.
132
run_method = self.run
133
def run_wrapper(*args, **kwargs):
135
return run_method(*args, **kwargs)
138
self.run = run_wrapper
140
self.mocker = Mocker()
142
self.__cleanup_funcs = []
143
self.__cleanup_paths = []
145
super(MockerTestCase, self).__init__(methodName)
148
for path in self.__cleanup_paths:
149
if os.path.isfile(path):
151
elif os.path.isdir(path):
154
for func, args, kwargs in self.__cleanup_funcs:
155
func(*args, **kwargs)
157
def addCleanup(self, func, *args, **kwargs):
158
self.__cleanup_funcs.append((func, args, kwargs))
160
def makeFile(self, content=None, suffix="", prefix="tmp", basename=None,
161
dirname=None, path=None):
162
"""Create a temporary file and return the path to it.
164
@param content: Initial content for the file.
165
@param suffix: Suffix to be given to the file's basename.
166
@param prefix: Prefix to be given to the file's basename.
167
@param basename: Full basename for the file.
168
@param dirname: Put file inside this directory.
170
The file is removed after the test runs.
173
self.__cleanup_paths.append(path)
174
elif basename is not None:
176
dirname = tempfile.mkdtemp()
177
self.__cleanup_paths.append(dirname)
178
path = os.path.join(dirname, basename)
180
fd, path = tempfile.mkstemp(suffix, prefix, dirname)
181
self.__cleanup_paths.append(path)
185
if content is not None:
186
file = open(path, "w")
191
def makeDir(self, suffix="", prefix="tmp", dirname=None, path=None):
192
"""Create a temporary directory and return the path to it.
194
@param suffix: Suffix to be given to the file's basename.
195
@param prefix: Prefix to be given to the file's basename.
196
@param dirname: Put directory inside this parent directory.
198
The directory is removed after the test runs.
203
path = tempfile.mkdtemp(suffix, prefix, dirname)
204
self.__cleanup_paths.append(path)
207
def failUnlessIs(self, first, second, msg=None):
208
"""Assert that C{first} is the same object as C{second}."""
209
if first is not second:
210
raise self.failureException(msg or "%r is not %r" % (first, second))
212
def failIfIs(self, first, second, msg=None):
213
"""Assert that C{first} is not the same object as C{second}."""
215
raise self.failureException(msg or "%r is %r" % (first, second))
217
def failUnlessIn(self, first, second, msg=None):
218
"""Assert that C{first} is contained in C{second}."""
219
if first not in second:
220
raise self.failureException(msg or "%r not in %r" % (first, second))
222
def failUnlessStartsWith(self, first, second, msg=None):
223
"""Assert that C{first} starts with C{second}."""
224
if first[:len(second)] != second:
225
raise self.failureException(msg or "%r doesn't start with %r" %
228
def failIfStartsWith(self, first, second, msg=None):
229
"""Assert that C{first} doesn't start with C{second}."""
230
if first[:len(second)] == second:
231
raise self.failureException(msg or "%r starts with %r" %
234
def failUnlessEndsWith(self, first, second, msg=None):
235
"""Assert that C{first} starts with C{second}."""
236
if first[len(first)-len(second):] != second:
237
raise self.failureException(msg or "%r doesn't end with %r" %
240
def failIfEndsWith(self, first, second, msg=None):
241
"""Assert that C{first} doesn't start with C{second}."""
242
if first[len(first)-len(second):] == second:
243
raise self.failureException(msg or "%r ends with %r" %
246
def failIfIn(self, first, second, msg=None):
247
"""Assert that C{first} is not contained in C{second}."""
249
raise self.failureException(msg or "%r in %r" % (first, second))
251
def failUnlessApproximates(self, first, second, tolerance, msg=None):
252
"""Assert that C{first} is near C{second} by at most C{tolerance}."""
253
if abs(first - second) > tolerance:
254
raise self.failureException(msg or "abs(%r - %r) > %r" %
255
(first, second, tolerance))
257
def failIfApproximates(self, first, second, tolerance, msg=None):
258
"""Assert that C{first} is far from C{second} by at least C{tolerance}.
260
if abs(first - second) <= tolerance:
261
raise self.failureException(msg or "abs(%r - %r) <= %r" %
262
(first, second, tolerance))
264
def failUnlessMethodsMatch(self, first, second):
265
"""Assert that public methods in C{first} are present in C{second}.
267
This method asserts that all public methods found in C{first} are also
268
present in C{second} and accept the same arguments. C{first} may
269
have its own private methods, though, and may not have all methods
270
found in C{second}. Note that if a private method in C{first} matches
271
the name of one in C{second}, their specification is still compared.
273
This is useful to verify if a fake or stub class have the same API as
274
the real class being simulated.
276
first_methods = dict(inspect.getmembers(first, inspect.ismethod))
277
second_methods = dict(inspect.getmembers(second, inspect.ismethod))
278
for name, first_method in first_methods.iteritems():
279
first_argspec = inspect.getargspec(first_method)
280
first_formatted = inspect.formatargspec(*first_argspec)
282
second_method = second_methods.get(name)
283
if second_method is None:
285
continue # First may have its own private methods.
286
raise self.failureException("%s.%s%s not present in %s" %
287
(first.__name__, name, first_formatted, second.__name__))
289
second_argspec = inspect.getargspec(second_method)
290
if first_argspec != second_argspec:
291
second_formatted = inspect.formatargspec(*second_argspec)
292
raise self.failureException("%s.%s%s != %s.%s%s" %
293
(first.__name__, name, first_formatted,
294
second.__name__, name, second_formatted))
297
assertIs = failUnlessIs
298
assertIsNot = failIfIs
299
assertIn = failUnlessIn
300
assertNotIn = failIfIn
301
assertStartsWith = failUnlessStartsWith
302
assertNotStartsWith = failIfStartsWith
303
assertEndsWith = failUnlessEndsWith
304
assertNotEndsWith = failIfEndsWith
305
assertApproximates = failUnlessApproximates
306
assertNotApproximates = failIfApproximates
307
assertMethodsMatch = failUnlessMethodsMatch
309
# The following are missing in Python < 2.4.
310
assertTrue = unittest.TestCase.failUnless
311
assertFalse = unittest.TestCase.failIf
313
# The following is provided for compatibility with Twisted's trial.
314
assertIdentical = assertIs
315
assertNotIdentical = assertIsNot
316
failUnlessIdentical = failUnlessIs
317
failIfIdentical = failIfIs
320
# --------------------------------------------------------------------
323
class classinstancemethod(object):
325
def __init__(self, method):
328
def __get__(self, obj, cls=None):
329
def bound_method(*args, **kwargs):
330
return self.method(cls, obj, *args, **kwargs)
334
class MockerBase(object):
335
"""Controller of mock objects.
337
A mocker instance is used to command recording and replay of
338
expectations on any number of mock objects.
340
Expectations should be expressed for the mock object while in
341
record mode (the initial one) by using the mock object itself,
342
and using the mocker (and/or C{expect()} as a helper) to define
343
additional behavior for each event. For instance::
349
assert mock.hello() == "Hi!"
353
In this short excerpt a mock object is being created, then an
354
expectation of a call to the C{hello()} method was recorded, and
355
when called the method should return the value C{10}. Then, the
356
mocker is put in replay mode, and the expectation is satisfied by
357
calling the C{hello()} method, which indeed returns 10. Finally,
358
a call to the L{restore()} method is performed to undo any needed
359
changes made in the environment, and the L{verify()} method is
360
called to ensure that all defined expectations were met.
362
The same logic can be expressed more elegantly using the
363
C{with mocker:} statement, as follows::
369
assert mock.hello() == "Hi!"
371
Also, the MockerTestCase class, which integrates the mocker on
372
a unittest.TestCase subclass, may be used to reduce the overhead
373
of controlling the mocker. A test could be written as follows::
375
class SampleTest(MockerTestCase):
377
def test_hello(self):
378
mock = self.mocker.mock()
380
self.mocker.result("Hi!")
382
self.assertEquals(mock.hello(), "Hi!")
387
# For convenience only.
390
class __metaclass__(type):
391
def __init__(self, name, bases, dict):
392
# Make independent lists on each subclass, inheriting from parent.
393
self._recorders = list(getattr(self, "_recorders", ()))
396
self._recorders = self._recorders[:]
398
self._recording = True
399
self._ordering = False
400
self._last_orderer = None
402
def is_recording(self):
403
"""Return True if in recording mode, False if in replay mode.
405
Recording is the initial state.
407
return self._recording
410
"""Change to replay mode, where recorded events are reproduced.
412
If already in replay mode, the mocker will be restored, with all
413
expectations reset, and then put again in replay mode.
415
An alternative and more comfortable way to replay changes is
416
using the 'with' statement, as follows::
423
The 'with' statement will automatically put mocker in replay
424
mode, and will also verify if all events were correctly reproduced
425
at the end (using L{verify()}), and also restore any changes done
426
in the environment (with L{restore()}).
428
Also check the MockerTestCase class, which integrates the
429
unittest.TestCase class with mocker.
431
if not self._recording:
432
for event in self._events:
435
self._recording = False
436
for event in self._events:
440
"""Restore changes in the environment, and return to recording mode.
442
This should always be called after the test is complete (succeeding
443
or not). There are ways to call this method automatically on
444
completion (e.g. using a C{with mocker:} statement, or using the
445
L{MockerTestCase} class.
447
if not self._recording:
448
self._recording = True
449
for event in self._events:
453
"""Reset the mocker state.
455
This will restore environment changes, if currently in replay
456
mode, and then remove all events previously recorded.
458
if not self._recording:
463
def get_events(self):
464
"""Return all recorded events."""
465
return self._events[:]
467
def add_event(self, event):
470
This method is used internally by the implementation, and
471
shouldn't be needed on normal mocker usage.
473
self._events.append(event)
475
orderer = event.add_task(Orderer(event.path))
476
if self._last_orderer:
477
orderer.add_dependency(self._last_orderer)
478
self._last_orderer = orderer
482
"""Check if all expectations were met, and raise AssertionError if not.
484
The exception message will include a nice description of which
485
expectations were not met, and why.
488
for event in self._events:
491
except AssertionError, e:
494
raise RuntimeError("Empty error message from %r"
498
message = [ERROR_PREFIX + "Unmet expectations:", ""]
500
lines = error.splitlines()
501
message.append("=> " + lines.pop(0))
502
message.extend([" " + line for line in lines])
504
raise AssertionError(os.linesep.join(message))
506
def mock(self, spec_and_type=None, spec=None, type=None,
507
name=None, count=True):
508
"""Return a new mock object.
510
@param spec_and_type: Handy positional argument which sets both
512
@param spec: Method calls will be checked for correctness against
514
@param type: If set, the Mock's __class__ attribute will return
515
the given type. This will make C{isinstance()} calls
517
@param name: Name for the mock object, used in the representation of
518
expressions. The name is rarely needed, as it's usually
519
guessed correctly from the variable name used.
520
@param count: If set to false, expressions may be executed any number
521
of times, unless an expectation is explicitly set using
522
the L{count()} method. By default, expressions are
525
if spec_and_type is not None:
526
spec = type = spec_and_type
527
return Mock(self, spec=spec, type=type, name=name, count=count)
529
def proxy(self, object, spec=True, type=True, name=None, count=True,
531
"""Return a new mock object which proxies to the given object.
533
Proxies are useful when only part of the behavior of an object
534
is to be mocked. Unknown expressions may be passed through to
535
the real implementation implicitly (if the C{passthrough} argument
536
is True), or explicitly (using the L{passthrough()} method
539
@param object: Real object to be proxied, and replaced by the mock
540
on replay mode. It may also be an "import path",
541
such as C{"time.time"}, in which case the object
542
will be the C{time} function from the C{time} module.
543
@param spec: Method calls will be checked for correctness against
544
the given object, which may be a class or an instance
545
where attributes will be looked up. Defaults to the
546
the C{object} parameter. May be set to None explicitly,
547
in which case spec checking is disabled. Checks may
548
also be disabled explicitly on a per-event basis with
549
the L{nospec()} method.
550
@param type: If set, the Mock's __class__ attribute will return
551
the given type. This will make C{isinstance()} calls
552
on the object work. Defaults to the type of the
553
C{object} parameter. May be set to None explicitly.
554
@param name: Name for the mock object, used in the representation of
555
expressions. The name is rarely needed, as it's usually
556
guessed correctly from the variable name used.
557
@param count: If set to false, expressions may be executed any number
558
of times, unless an expectation is explicitly set using
559
the L{count()} method. By default, expressions are
561
@param passthrough: If set to False, passthrough of actions on the
562
proxy to the real object will only happen when
563
explicitly requested via the L{passthrough()}
566
if isinstance(object, basestring):
569
import_stack = object.split(".")
572
module_path = ".".join(import_stack)
574
object = __import__(module_path, {}, {}, [""])
576
attr_stack.insert(0, import_stack.pop())
581
for attr in attr_stack:
582
object = getattr(object, attr)
587
type = __builtin__.type(object)
588
return Mock(self, spec=spec, type=type, object=object,
589
name=name, count=count, passthrough=passthrough)
591
def replace(self, object, spec=True, type=True, name=None, count=True,
593
"""Create a proxy, and replace the original object with the mock.
595
On replay, the original object will be replaced by the returned
596
proxy in all dictionaries found in the running interpreter via
597
the garbage collecting system. This should cover module
598
namespaces, class namespaces, instance namespaces, and so on.
600
@param object: Real object to be proxied, and replaced by the mock
601
on replay mode. It may also be an "import path",
602
such as C{"time.time"}, in which case the object
603
will be the C{time} function from the C{time} module.
604
@param spec: Method calls will be checked for correctness against
605
the given object, which may be a class or an instance
606
where attributes will be looked up. Defaults to the
607
the C{object} parameter. May be set to None explicitly,
608
in which case spec checking is disabled. Checks may
609
also be disabled explicitly on a per-event basis with
610
the L{nospec()} method.
611
@param type: If set, the Mock's __class__ attribute will return
612
the given type. This will make C{isinstance()} calls
613
on the object work. Defaults to the type of the
614
C{object} parameter. May be set to None explicitly.
615
@param name: Name for the mock object, used in the representation of
616
expressions. The name is rarely needed, as it's usually
617
guessed correctly from the variable name used.
618
@param passthrough: If set to False, passthrough of actions on the
619
proxy to the real object will only happen when
620
explicitly requested via the L{passthrough()}
623
mock = self.proxy(object, spec, type, name, count, passthrough)
624
event = self._get_replay_restore_event()
625
event.add_task(ProxyReplacer(mock))
628
def patch(self, object, spec=True):
629
"""Patch an existing object to reproduce recorded events.
631
@param object: Class or instance to be patched.
632
@param spec: Method calls will be checked for correctness against
633
the given object, which may be a class or an instance
634
where attributes will be looked up. Defaults to the
635
the C{object} parameter. May be set to None explicitly,
636
in which case spec checking is disabled. Checks may
637
also be disabled explicitly on a per-event basis with
638
the L{nospec()} method.
640
The result of this method is still a mock object, which can be
641
used like any other mock object to record events. The difference
642
is that when the mocker is put on replay mode, the *real* object
643
will be modified to behave according to recorded expectations.
645
Patching works in individual instances, and also in classes.
646
When an instance is patched, recorded events will only be
647
considered on this specific instance, and other instances should
648
behave normally. When a class is patched, the reproduction of
649
events will be considered on any instance of this class once
650
created (collectively).
652
Observe that, unlike with proxies which catch only events done
653
through the mock object, *all* accesses to recorded expectations
654
will be considered; even these coming from the object itself
655
(e.g. C{self.hello()} is considered if this method was patched).
656
While this is a very powerful feature, and many times the reason
657
to use patches in the first place, it's important to keep this
660
Patching of the original object only takes place when the mocker
661
is put on replay mode, and the patched object will be restored
662
to its original state once the L{restore()} method is called
663
(explicitly, or implicitly with alternative conventions, such as
664
a C{with mocker:} block, or a MockerTestCase class).
669
event = self._get_replay_restore_event()
670
event.add_task(patcher)
671
mock = Mock(self, object=object, patcher=patcher,
672
passthrough=True, spec=spec)
673
patcher.patch_attr(object, '__mocker_mock__', mock)
677
"""This is called by mock objects whenever something happens to them.
679
This method is part of the implementation between the mocker
683
event = self.add_event(Event(path))
684
for recorder in self._recorders:
685
recorder(self, event)
686
return Mock(self, path)
688
# First run events that may run, then run unsatisfied events, then
689
# ones not previously run. We put the index in the ordering tuple
690
# instead of the actual event because we want a stable sort
691
# (ordering between 2 events is undefined).
692
events = self._events
693
order = [(events[i].satisfied()*2 + events[i].has_run(), i)
694
for i in range(len(events))]
697
for weight, i in order:
699
if event.matches(path):
700
if event.may_run(path):
701
return event.run(path)
702
elif postponed is None:
704
if postponed is not None:
705
return postponed.run(path)
706
raise MatchError(ERROR_PREFIX + "Unexpected expression: %s" % path)
708
def get_recorders(cls, self):
709
"""Return recorders associated with this mocker class or instance.
711
This method may be called on mocker instances and also on mocker
712
classes. See the L{add_recorder()} method for more information.
714
return (self or cls)._recorders[:]
715
get_recorders = classinstancemethod(get_recorders)
717
def add_recorder(cls, self, recorder):
718
"""Add a recorder to this mocker class or instance.
720
@param recorder: Callable accepting C{(mocker, event)} as parameters.
722
This is part of the implementation of mocker.
724
All registered recorders are called for translating events that
725
happen during recording into expectations to be met once the state
726
is switched to replay mode.
728
This method may be called on mocker instances and also on mocker
729
classes. When called on a class, the recorder will be used by
730
all instances, and also inherited on subclassing. When called on
731
instances, the recorder is added only to the given instance.
733
(self or cls)._recorders.append(recorder)
735
add_recorder = classinstancemethod(add_recorder)
737
def remove_recorder(cls, self, recorder):
738
"""Remove the given recorder from this mocker class or instance.
740
This method may be called on mocker classes and also on mocker
741
instances. See the L{add_recorder()} method for more information.
743
(self or cls)._recorders.remove(recorder)
744
remove_recorder = classinstancemethod(remove_recorder)
746
def result(self, value):
747
"""Make the last recorded event return the given value on replay.
749
@param value: Object to be returned when the event is replayed.
751
self.call(lambda *args, **kwargs: value)
753
def generate(self, sequence):
754
"""Last recorded event will return a generator with the given sequence.
756
@param sequence: Sequence of values to be generated.
758
def generate(*args, **kwargs):
759
for value in sequence:
763
def throw(self, exception):
764
"""Make the last recorded event raise the given exception on replay.
766
@param exception: Class or instance of exception to be raised.
768
def raise_exception(*args, **kwargs):
770
self.call(raise_exception)
772
def call(self, func):
773
"""Make the last recorded event cause the given function to be called.
775
@param func: Function to be called.
777
The result of the function will be used as the event result.
779
self._events[-1].add_task(FunctionRunner(func))
781
def count(self, min, max=False):
782
"""Last recorded event must be replayed between min and max times.
784
@param min: Minimum number of times that the event must happen.
785
@param max: Maximum number of times that the event must happen. If
786
not given, it defaults to the same value of the C{min}
787
parameter. If set to None, there is no upper limit, and
788
the expectation is met as long as it happens at least
791
event = self._events[-1]
792
for task in event.get_tasks():
793
if isinstance(task, RunCounter):
794
event.remove_task(task)
795
event.add_task(RunCounter(min, max))
797
def is_ordering(self):
798
"""Return true if all events are being ordered.
800
See the L{order()} method.
802
return self._ordering
805
"""Disable the ordered mode.
807
See the L{order()} method for more information.
809
self._ordering = False
810
self._last_orderer = None
812
def order(self, *path_holders):
813
"""Create an expectation of order between two or more events.
815
@param path_holders: Objects returned as the result of recorded events.
817
By default, mocker won't force events to happen precisely in
818
the order they were recorded. Calling this method will change
819
this behavior so that events will only match if reproduced in
822
There are two ways in which this method may be used. Which one
823
is used in a given occasion depends only on convenience.
825
If no arguments are passed, the mocker will be put in a mode where
826
all the recorded events following the method call will only be met
827
if they happen in order. When that's used, the mocker may be put
828
back in unordered mode by calling the L{unorder()} method, or by
829
using a 'with' block, like so::
831
with mocker.ordered():
834
In this case, only expressions in <record events> will be ordered,
835
and the mocker will be back in unordered mode after the 'with' block.
837
The second way to use it is by specifying precisely which events
838
should be ordered. As an example::
844
mocker.order(expr1, expr2, expr3)
846
This method of ordering only works when the expression returns
849
Also check the L{after()} and L{before()} methods, which are
850
alternative ways to perform this.
853
self._ordering = True
854
return OrderedContext(self)
857
for path_holder in path_holders:
858
if type(path_holder) is Path:
861
path = path_holder.__mocker_path__
862
for event in self._events:
863
if event.path is path:
864
for task in event.get_tasks():
865
if isinstance(task, Orderer):
869
orderer = Orderer(path)
870
event.add_task(orderer)
872
orderer.add_dependency(last_orderer)
873
last_orderer = orderer
876
def after(self, *path_holders):
877
"""Last recorded event must happen after events referred to.
879
@param path_holders: Objects returned as the result of recorded events
880
which should happen before the last recorded event
882
As an example, the idiom::
884
expect(mock.x).after(mock.y, mock.z)
886
is an alternative way to say::
891
mocker.order(expr_y, expr_x)
892
mocker.order(expr_z, expr_x)
894
See L{order()} for more information.
896
last_path = self._events[-1].path
897
for path_holder in path_holders:
898
self.order(path_holder, last_path)
900
def before(self, *path_holders):
901
"""Last recorded event must happen before events referred to.
903
@param path_holders: Objects returned as the result of recorded events
904
which should happen after the last recorded event
906
As an example, the idiom::
908
expect(mock.x).before(mock.y, mock.z)
910
is an alternative way to say::
915
mocker.order(expr_x, expr_y)
916
mocker.order(expr_x, expr_z)
918
See L{order()} for more information.
920
last_path = self._events[-1].path
921
for path_holder in path_holders:
922
self.order(last_path, path_holder)
925
"""Don't check method specification of real object on last event.
927
By default, when using a mock created as the result of a call to
928
L{proxy()}, L{replace()}, and C{patch()}, or when passing the spec
929
attribute to the L{mock()} method, method calls on the given object
930
are checked for correctness against the specification of the real
931
object (or the explicitly provided spec).
933
This method will disable that check specifically for the last
936
event = self._events[-1]
937
for task in event.get_tasks():
938
if isinstance(task, SpecChecker):
939
event.remove_task(task)
941
def passthrough(self, result_callback=None):
942
"""Make the last recorded event run on the real object once seen.
944
@param result_callback: If given, this function will be called with
945
the result of the *real* method call as the only argument.
947
This can only be used on proxies, as returned by the L{proxy()}
948
and L{replace()} methods, or on mocks representing patched objects,
949
as returned by the L{patch()} method.
951
event = self._events[-1]
952
if event.path.root_object is None:
953
raise TypeError("Mock object isn't a proxy")
954
event.add_task(PathExecuter(result_callback))
957
"""Enter in a 'with' context. This will run replay()."""
961
def __exit__(self, type, value, traceback):
962
"""Exit from a 'with' context.
964
This will run restore() at all times, but will only run verify()
965
if the 'with' block itself hasn't raised an exception. Exceptions
966
in that block are never swallowed.
973
def _get_replay_restore_event(self):
974
"""Return unique L{ReplayRestoreEvent}, creating if needed.
976
Some tasks only want to replay/restore. When that's the case,
977
they shouldn't act on other events during replay. Also, they
978
can all be put in a single event when that's the case. Thus,
979
we add a single L{ReplayRestoreEvent} as the first element of
982
if not self._events or type(self._events[0]) != ReplayRestoreEvent:
983
self._events.insert(0, ReplayRestoreEvent())
984
return self._events[0]
987
class OrderedContext(object):
989
def __init__(self, mocker):
990
self._mocker = mocker
995
def __exit__(self, type, value, traceback):
996
self._mocker.unorder()
999
class Mocker(MockerBase):
1000
__doc__ = MockerBase.__doc__
1002
# Decorator to add recorders on the standard Mocker class.
1003
recorder = Mocker.add_recorder
1006
# --------------------------------------------------------------------
1011
def __init__(self, mocker, path=None, name=None, spec=None, type=None,
1012
object=None, passthrough=False, patcher=None, count=True):
1013
self.__mocker__ = mocker
1014
self.__mocker_path__ = path or Path(self, object)
1015
self.__mocker_name__ = name
1016
self.__mocker_spec__ = spec
1017
self.__mocker_object__ = object
1018
self.__mocker_passthrough__ = passthrough
1019
self.__mocker_patcher__ = patcher
1020
self.__mocker_replace__ = False
1021
self.__mocker_type__ = type
1022
self.__mocker_count__ = count
1024
def __mocker_act__(self, kind, args=(), kwargs={}, object=None):
1025
if self.__mocker_name__ is None:
1026
self.__mocker_name__ = find_object_name(self, 2)
1027
action = Action(kind, args, kwargs, self.__mocker_path__)
1028
path = self.__mocker_path__ + action
1029
if object is not None:
1030
path.root_object = object
1032
return self.__mocker__.act(path)
1033
except MatchError, exception:
1034
root_mock = path.root_mock
1035
if (path.root_object is not None and
1036
root_mock.__mocker_passthrough__):
1037
return path.execute(path.root_object)
1038
# Reinstantiate to show raise statement on traceback, and
1039
# also to make the traceback shown shorter.
1040
raise MatchError(str(exception))
1041
except AssertionError, e:
1042
lines = str(e).splitlines()
1043
message = [ERROR_PREFIX + "Unmet expectation:", ""]
1044
message.append("=> " + lines.pop(0))
1045
message.extend([" " + line for line in lines])
1047
raise AssertionError(os.linesep.join(message))
1049
def __getattribute__(self, name):
1050
if name.startswith("__mocker_"):
1051
return super(Mock, self).__getattribute__(name)
1052
if name == "__class__":
1053
if self.__mocker__.is_recording() or self.__mocker_type__ is None:
1055
return self.__mocker_type__
1056
return self.__mocker_act__("getattr", (name,))
1058
def __setattr__(self, name, value):
1059
if name.startswith("__mocker_"):
1060
return super(Mock, self).__setattr__(name, value)
1061
return self.__mocker_act__("setattr", (name, value))
1063
def __delattr__(self, name):
1064
return self.__mocker_act__("delattr", (name,))
1066
def __call__(self, *args, **kwargs):
1067
return self.__mocker_act__("call", args, kwargs)
1069
def __contains__(self, value):
1070
return self.__mocker_act__("contains", (value,))
1072
def __getitem__(self, key):
1073
return self.__mocker_act__("getitem", (key,))
1075
def __setitem__(self, key, value):
1076
return self.__mocker_act__("setitem", (key, value))
1078
def __delitem__(self, key):
1079
return self.__mocker_act__("delitem", (key,))
1082
# MatchError is turned on an AttributeError so that list() and
1083
# friends act properly when trying to get length hints on
1084
# something that doesn't offer them.
1086
result = self.__mocker_act__("len")
1087
except MatchError, e:
1088
raise AttributeError(str(e))
1089
if type(result) is Mock:
1093
def __nonzero__(self):
1095
return self.__mocker_act__("nonzero")
1096
except MatchError, e:
1100
# XXX On py3k, when next() becomes __next__(), we'll be able
1101
# to return the mock itself because it will be considered
1102
# an iterator (we'll be mocking __next__ as well, which we
1104
result = self.__mocker_act__("iter")
1105
if type(result) is Mock:
1109
# When adding a new action kind here, also add support for it on
1110
# Action.execute() and Path.__str__().
1113
def find_object_name(obj, depth=0):
1114
"""Try to detect how the object is named on a previous scope."""
1116
frame = sys._getframe(depth+1)
1119
for name, frame_obj in frame.f_locals.iteritems():
1120
if frame_obj is obj:
1122
self = frame.f_locals.get("self")
1123
if self is not None:
1125
items = list(self.__dict__.iteritems())
1129
for name, self_obj in items:
1135
# --------------------------------------------------------------------
1138
class Action(object):
1140
def __init__(self, kind, args, kwargs, path=None):
1143
self.kwargs = kwargs
1145
self._execute_cache = {}
1148
if self.path is None:
1149
return "Action(%r, %r, %r)" % (self.kind, self.args, self.kwargs)
1150
return "Action(%r, %r, %r, %r)" % \
1151
(self.kind, self.args, self.kwargs, self.path)
1153
def __eq__(self, other):
1154
return (self.kind == other.kind and
1155
self.args == other.args and
1156
self.kwargs == other.kwargs)
1158
def __ne__(self, other):
1159
return not self.__eq__(other)
1161
def matches(self, other):
1162
return (self.kind == other.kind and
1163
match_params(self.args, self.kwargs, other.args, other.kwargs))
1165
def execute(self, object):
1166
# This caching scheme may fail if the object gets deallocated before
1167
# the action, as the id might get reused. It's somewhat easy to fix
1168
# that with a weakref callback. For our uses, though, the object
1169
# should never get deallocated before the action itself, so we'll
1170
# just keep it simple.
1171
if id(object) in self._execute_cache:
1172
return self._execute_cache[id(object)]
1173
execute = getattr(object, "__mocker_execute__", None)
1174
if execute is not None:
1175
result = execute(self, object)
1178
if kind == "getattr":
1179
result = getattr(object, self.args[0])
1180
elif kind == "setattr":
1181
result = setattr(object, self.args[0], self.args[1])
1182
elif kind == "delattr":
1183
result = delattr(object, self.args[0])
1184
elif kind == "call":
1185
result = object(*self.args, **self.kwargs)
1186
elif kind == "contains":
1187
result = self.args[0] in object
1188
elif kind == "getitem":
1189
result = object[self.args[0]]
1190
elif kind == "setitem":
1191
result = object[self.args[0]] = self.args[1]
1192
elif kind == "delitem":
1193
del object[self.args[0]]
1196
result = len(object)
1197
elif kind == "nonzero":
1198
result = bool(object)
1199
elif kind == "iter":
1200
result = iter(object)
1202
raise RuntimeError("Don't know how to execute %r kind." % kind)
1203
self._execute_cache[id(object)] = result
1209
def __init__(self, root_mock, root_object=None, actions=()):
1210
self.root_mock = root_mock
1211
self.root_object = root_object
1212
self.actions = tuple(actions)
1213
self.__mocker_replace__ = False
1215
def parent_path(self):
1216
if not self.actions:
1218
return self.actions[-1].path
1219
parent_path = property(parent_path)
1221
def __add__(self, action):
1222
"""Return a new path which includes the given action at the end."""
1223
return self.__class__(self.root_mock, self.root_object,
1224
self.actions + (action,))
1226
def __eq__(self, other):
1227
"""Verify if the two paths are equal.
1229
Two paths are equal if they refer to the same mock object, and
1230
have the actions with equal kind, args and kwargs.
1232
if (self.root_mock is not other.root_mock or
1233
self.root_object is not other.root_object or
1234
len(self.actions) != len(other.actions)):
1236
for action, other_action in zip(self.actions, other.actions):
1237
if action != other_action:
1241
def matches(self, other):
1242
"""Verify if the two paths are equivalent.
1244
Two paths are equal if they refer to the same mock object, and
1245
have the same actions performed on them.
1247
if (self.root_mock is not other.root_mock or
1248
len(self.actions) != len(other.actions)):
1250
for action, other_action in zip(self.actions, other.actions):
1251
if not action.matches(other_action):
1255
def execute(self, object):
1256
"""Execute all actions sequentially on object, and return result.
1258
for action in self.actions:
1259
object = action.execute(object)
1263
"""Transform the path into a nice string such as obj.x.y('z')."""
1264
result = self.root_mock.__mocker_name__ or "<mock>"
1265
for action in self.actions:
1266
if action.kind == "getattr":
1267
result = "%s.%s" % (result, action.args[0])
1268
elif action.kind == "setattr":
1269
result = "%s.%s = %r" % (result, action.args[0], action.args[1])
1270
elif action.kind == "delattr":
1271
result = "del %s.%s" % (result, action.args[0])
1272
elif action.kind == "call":
1273
args = [repr(x) for x in action.args]
1274
items = list(action.kwargs.iteritems())
1277
args.append("%s=%r" % pair)
1278
result = "%s(%s)" % (result, ", ".join(args))
1279
elif action.kind == "contains":
1280
result = "%r in %s" % (action.args[0], result)
1281
elif action.kind == "getitem":
1282
result = "%s[%r]" % (result, action.args[0])
1283
elif action.kind == "setitem":
1284
result = "%s[%r] = %r" % (result, action.args[0],
1286
elif action.kind == "delitem":
1287
result = "del %s[%r]" % (result, action.args[0])
1288
elif action.kind == "len":
1289
result = "len(%s)" % result
1290
elif action.kind == "nonzero":
1291
result = "bool(%s)" % result
1292
elif action.kind == "iter":
1293
result = "iter(%s)" % result
1295
raise RuntimeError("Don't know how to format kind %r" %
1300
class SpecialArgument(object):
1301
"""Base for special arguments for matching parameters."""
1303
def __init__(self, object=None):
1304
self.object = object
1307
if self.object is None:
1308
return self.__class__.__name__
1310
return "%s(%r)" % (self.__class__.__name__, self.object)
1312
def matches(self, other):
1315
def __eq__(self, other):
1316
return type(other) == type(self) and self.object == other.object
1319
class ANY(SpecialArgument):
1320
"""Matches any single argument."""
1325
class ARGS(SpecialArgument):
1326
"""Matches zero or more positional arguments."""
1331
class KWARGS(SpecialArgument):
1332
"""Matches zero or more keyword arguments."""
1337
class IS(SpecialArgument):
1339
def matches(self, other):
1340
return self.object is other
1342
def __eq__(self, other):
1343
return type(other) == type(self) and self.object is other.object
1346
class CONTAINS(SpecialArgument):
1348
def matches(self, other):
1351
except AttributeError:
1355
# If an object can't be iterated, and has no __contains__
1356
# hook, it'd blow up on the test below. We test this in
1357
# advance to prevent catching more errors than we really
1360
return self.object in other
1363
class IN(SpecialArgument):
1365
def matches(self, other):
1366
return other in self.object
1369
class MATCH(SpecialArgument):
1371
def matches(self, other):
1372
return bool(self.object(other))
1374
def __eq__(self, other):
1375
return type(other) == type(self) and self.object is other.object
1378
def match_params(args1, kwargs1, args2, kwargs2):
1379
"""Match the two sets of parameters, considering special parameters."""
1381
has_args = ARGS in args1
1382
has_kwargs = KWARGS in args1
1385
args1 = [arg1 for arg1 in args1 if arg1 is not KWARGS]
1386
elif len(kwargs1) != len(kwargs2):
1389
if not has_args and len(args1) != len(args2):
1392
# Either we have the same number of kwargs, or unknown keywords are
1393
# accepted (KWARGS was used), so check just the ones in kwargs1.
1394
for key, arg1 in kwargs1.iteritems():
1395
if key not in kwargs2:
1398
if isinstance(arg1, SpecialArgument):
1399
if not arg1.matches(arg2):
1404
# Keywords match. Now either we have the same number of
1405
# arguments, or ARGS was used. If ARGS wasn't used, arguments
1406
# must match one-on-one necessarily.
1408
for arg1, arg2 in zip(args1, args2):
1409
if isinstance(arg1, SpecialArgument):
1410
if not arg1.matches(arg2):
1416
# Easy choice. Keywords are matching, and anything on args is accepted.
1417
if (ARGS,) == args1:
1420
# We have something different there. If we don't have positional
1421
# arguments on the original call, it can't match.
1423
# Unless we have just several ARGS (which is bizarre, but..).
1425
if arg1 is not ARGS:
1429
# Ok, all bets are lost. We have to actually do the more expensive
1430
# matching. This is an algorithm based on the idea of the Levenshtein
1431
# Distance between two strings, but heavily hacked for this purpose.
1433
if args1[0] is ARGS:
1438
for i in range(len(args1)):
1440
if args1[i] is ARGS:
1441
for j in range(1, args2l):
1442
last, array[j] = array[j], min(array[j-1], array[j], last)
1444
array[0] = i or int(args1[i] != args2[0])
1445
for j in range(1, args2l):
1446
last, array[j] = array[j], last or int(args1[i] != args2[j])
1454
# --------------------------------------------------------------------
1455
# Event and task base.
1457
class Event(object):
1458
"""Aggregation of tasks that keep track of a recorded action.
1460
An event represents something that may or may not happen while the
1461
mocked environment is running, such as an attribute access, or a
1462
method call. The event is composed of several tasks that are
1463
orchestrated together to create a composed meaning for the event,
1464
including for which actions it should be run, what happens when it
1465
runs, and what's the expectations about the actions run.
1468
def __init__(self, path=None):
1471
self._has_run = False
1473
def add_task(self, task):
1474
"""Add a new task to this taks."""
1475
self._tasks.append(task)
1478
def remove_task(self, task):
1479
self._tasks.remove(task)
1481
def get_tasks(self):
1482
return self._tasks[:]
1484
def matches(self, path):
1485
"""Return true if *all* tasks match the given path."""
1486
for task in self._tasks:
1487
if not task.matches(path):
1489
return bool(self._tasks)
1492
return self._has_run
1494
def may_run(self, path):
1495
"""Verify if any task would certainly raise an error if run.
1497
This will call the C{may_run()} method on each task and return
1498
false if any of them returns false.
1500
for task in self._tasks:
1501
if not task.may_run(path):
1505
def run(self, path):
1506
"""Run all tasks with the given action.
1508
@param path: The path of the expression run.
1510
Running an event means running all of its tasks individually and in
1511
order. An event should only ever be run if all of its tasks claim to
1512
match the given action.
1514
The result of this method will be the last result of a task
1515
which isn't None, or None if they're all None.
1517
self._has_run = True
1520
for task in self._tasks:
1522
task_result = task.run(path)
1523
except AssertionError, e:
1526
raise RuntimeError("Empty error message from %r" % task)
1527
errors.append(error)
1529
if task_result is not None:
1530
result = task_result
1532
message = [str(self.path)]
1533
if str(path) != message[0]:
1534
message.append("- Run: %s" % path)
1535
for error in errors:
1536
lines = error.splitlines()
1537
message.append("- " + lines.pop(0))
1538
message.extend([" " + line for line in lines])
1539
raise AssertionError(os.linesep.join(message))
1542
def satisfied(self):
1543
"""Return true if all tasks are satisfied.
1545
Being satisfied means that there are no unmet expectations.
1547
for task in self._tasks:
1550
except AssertionError:
1555
"""Run verify on all tasks.
1557
The verify method is supposed to raise an AssertionError if the
1558
task has unmet expectations, with a one-line explanation about
1559
why this item is unmet. This method should be safe to be called
1560
multiple times without side effects.
1563
for task in self._tasks:
1566
except AssertionError, e:
1569
raise RuntimeError("Empty error message from %r" % task)
1570
errors.append(error)
1572
message = [str(self.path)]
1573
for error in errors:
1574
lines = error.splitlines()
1575
message.append("- " + lines.pop(0))
1576
message.extend([" " + line for line in lines])
1577
raise AssertionError(os.linesep.join(message))
1580
"""Put all tasks in replay mode."""
1581
self._has_run = False
1582
for task in self._tasks:
1586
"""Restore the state of all tasks."""
1587
for task in self._tasks:
1591
class ReplayRestoreEvent(Event):
1592
"""Helper event for tasks which need replay/restore but shouldn't match."""
1594
def matches(self, path):
1599
"""Element used to track one specific aspect on an event.
1601
A task is responsible for adding any kind of logic to an event.
1602
Examples of that are counting the number of times the event was
1603
made, verifying parameters if any, and so on.
1606
def matches(self, path):
1607
"""Return true if the task is supposed to be run for the given path.
1611
def may_run(self, path):
1612
"""Return false if running this task would certainly raise an error."""
1615
def run(self, path):
1616
"""Perform the task item, considering that the given action happened.
1620
"""Raise AssertionError if expectations for this item are unmet.
1622
The verify method is supposed to raise an AssertionError if the
1623
task has unmet expectations, with a one-line explanation about
1624
why this item is unmet. This method should be safe to be called
1625
multiple times without side effects.
1629
"""Put the task in replay mode.
1631
Any expectations of the task should be reset.
1635
"""Restore any environmental changes made by the task.
1637
Verify should continue to work after this is called.
1641
# --------------------------------------------------------------------
1642
# Task implementations.
1644
class OnRestoreCaller(Task):
1645
"""Call a given callback when restoring."""
1647
def __init__(self, callback):
1648
self._callback = callback
1654
class PathMatcher(Task):
1655
"""Match the action path against a given path."""
1657
def __init__(self, path):
1660
def matches(self, path):
1661
return self.path.matches(path)
1663
def path_matcher_recorder(mocker, event):
1664
event.add_task(PathMatcher(event.path))
1666
Mocker.add_recorder(path_matcher_recorder)
1669
class RunCounter(Task):
1670
"""Task which verifies if the number of runs are within given boundaries.
1673
def __init__(self, min, max=False):
1676
self.max = sys.maxint
1686
def may_run(self, path):
1687
return self._runs < self.max
1689
def run(self, path):
1691
if self._runs > self.max:
1695
if not self.min <= self._runs <= self.max:
1696
if self._runs < self.min:
1697
raise AssertionError("Performed fewer times than expected.")
1698
raise AssertionError("Performed more times than expected.")
1701
class ImplicitRunCounter(RunCounter):
1702
"""RunCounter inserted by default on any event.
1704
This is a way to differentiate explicitly added counters and
1708
def run_counter_recorder(mocker, event):
1709
"""Any event may be repeated once, unless disabled by default."""
1710
if event.path.root_mock.__mocker_count__:
1711
event.add_task(ImplicitRunCounter(1))
1713
Mocker.add_recorder(run_counter_recorder)
1715
def run_counter_removal_recorder(mocker, event):
1717
Events created by getattr actions which lead to other events
1718
may be repeated any number of times. For that, we remove implicit
1719
run counters of any getattr actions leading to the current one.
1721
parent_path = event.path.parent_path
1722
for event in mocker.get_events()[::-1]:
1723
if (event.path is parent_path and
1724
event.path.actions[-1].kind == "getattr"):
1725
for task in event.get_tasks():
1726
if type(task) is ImplicitRunCounter:
1727
event.remove_task(task)
1729
Mocker.add_recorder(run_counter_removal_recorder)
1732
class MockReturner(Task):
1733
"""Return a mock based on the action path."""
1735
def __init__(self, mocker):
1736
self.mocker = mocker
1738
def run(self, path):
1739
return Mock(self.mocker, path)
1741
def mock_returner_recorder(mocker, event):
1742
"""Events that lead to other events must return mock objects."""
1743
parent_path = event.path.parent_path
1744
for event in mocker.get_events():
1745
if event.path is parent_path:
1746
for task in event.get_tasks():
1747
if isinstance(task, MockReturner):
1750
event.add_task(MockReturner(mocker))
1753
Mocker.add_recorder(mock_returner_recorder)
1756
class FunctionRunner(Task):
1757
"""Task that runs a function everything it's run.
1759
Arguments of the last action in the path are passed to the function,
1760
and the function result is also returned.
1763
def __init__(self, func):
1766
def run(self, path):
1767
action = path.actions[-1]
1768
return self._func(*action.args, **action.kwargs)
1771
class PathExecuter(Task):
1772
"""Task that executes a path in the real object, and returns the result."""
1774
def __init__(self, result_callback=None):
1775
self._result_callback = result_callback
1777
def get_result_callback(self):
1778
return self._result_callback
1780
def run(self, path):
1781
result = path.execute(path.root_object)
1782
if self._result_callback is not None:
1783
self._result_callback(result)
1787
class Orderer(Task):
1788
"""Task to establish an order relation between two events.
1790
An orderer task will only match once all its dependencies have
1794
def __init__(self, path):
1797
self._dependencies = []
1805
def may_run(self, path):
1806
for dependency in self._dependencies:
1807
if not dependency.has_run():
1811
def run(self, path):
1812
for dependency in self._dependencies:
1813
if not dependency.has_run():
1814
raise AssertionError("Should be after: %s" % dependency.path)
1817
def add_dependency(self, orderer):
1818
self._dependencies.append(orderer)
1820
def get_dependencies(self):
1821
return self._dependencies
1824
class SpecChecker(Task):
1825
"""Task to check if arguments of the last action conform to a real method.
1828
def __init__(self, method):
1829
self._method = method
1830
self._unsupported = False
1834
self._args, self._varargs, self._varkwargs, self._defaults = \
1835
inspect.getargspec(method)
1837
self._unsupported = True
1839
if self._defaults is None:
1841
if type(method) is type(self.run):
1842
self._args = self._args[1:]
1844
def get_method(self):
1847
def _raise(self, message):
1848
spec = inspect.formatargspec(self._args, self._varargs,
1849
self._varkwargs, self._defaults)
1850
raise AssertionError("Specification is %s%s: %s" %
1851
(self._method.__name__, spec, message))
1854
if not self._method:
1855
raise AssertionError("Method not found in real specification")
1857
def may_run(self, path):
1860
except AssertionError:
1864
def run(self, path):
1865
if not self._method:
1866
raise AssertionError("Method not found in real specification")
1867
if self._unsupported:
1868
return # Can't check it. Happens with builtin functions. :-(
1869
action = path.actions[-1]
1870
obtained_len = len(action.args)
1871
obtained_kwargs = action.kwargs.copy()
1872
nodefaults_len = len(self._args) - len(self._defaults)
1873
for i, name in enumerate(self._args):
1874
if i < obtained_len and name in action.kwargs:
1875
self._raise("%r provided twice" % name)
1876
if (i >= obtained_len and i < nodefaults_len and
1877
name not in action.kwargs):
1878
self._raise("%r not provided" % name)
1879
obtained_kwargs.pop(name, None)
1880
if obtained_len > len(self._args) and not self._varargs:
1881
self._raise("too many args provided")
1882
if obtained_kwargs and not self._varkwargs:
1883
self._raise("unknown kwargs: %s" % ", ".join(obtained_kwargs))
1885
def spec_checker_recorder(mocker, event):
1886
spec = event.path.root_mock.__mocker_spec__
1888
actions = event.path.actions
1889
if len(actions) == 1:
1890
if actions[0].kind == "call":
1891
method = getattr(spec, "__call__", None)
1892
event.add_task(SpecChecker(method))
1893
elif len(actions) == 2:
1894
if actions[0].kind == "getattr" and actions[1].kind == "call":
1895
method = getattr(spec, actions[0].args[0], None)
1896
event.add_task(SpecChecker(method))
1898
Mocker.add_recorder(spec_checker_recorder)
1901
class ProxyReplacer(Task):
1902
"""Task which installs and deinstalls proxy mocks.
1904
This task will replace a real object by a mock in all dictionaries
1905
found in the running interpreter via the garbage collecting system.
1908
def __init__(self, mock):
1910
self.__mocker_replace__ = False
1913
global_replace(self.mock.__mocker_object__, self.mock)
1916
global_replace(self.mock, self.mock.__mocker_object__)
1919
def global_replace(remove, install):
1920
"""Replace object 'remove' with object 'install' on all dictionaries."""
1921
for referrer in gc.get_referrers(remove):
1922
if (type(referrer) is dict and
1923
referrer.get("__mocker_replace__", True)):
1924
for key, value in list(referrer.iteritems()):
1926
referrer[key] = install
1929
class Undefined(object):
1934
Undefined = Undefined()
1937
class Patcher(Task):
1940
super(Patcher, self).__init__()
1941
self._monitored = {} # {kind: {id(object): object}}
1944
def is_monitoring(self, obj, kind):
1945
monitored = self._monitored.get(kind)
1947
if id(obj) in monitored:
1950
if issubclass(cls, type):
1952
bases = set([id(base) for base in cls.__mro__])
1953
bases.intersection_update(monitored)
1957
def monitor(self, obj, kind):
1958
if kind not in self._monitored:
1959
self._monitored[kind] = {}
1960
self._monitored[kind][id(obj)] = obj
1962
def patch_attr(self, obj, attr, value):
1963
original = obj.__dict__.get(attr, Undefined)
1964
self._patched[id(obj), attr] = obj, attr, original
1965
setattr(obj, attr, value)
1967
def get_unpatched_attr(self, obj, attr):
1969
if issubclass(cls, type):
1972
for mro_cls in cls.__mro__:
1973
key = (id(mro_cls), attr)
1974
if key in self._patched:
1975
result = self._patched[key][2]
1976
if result is not Undefined:
1978
elif attr in mro_cls.__dict__:
1979
result = mro_cls.__dict__.get(attr, Undefined)
1981
if isinstance(result, object) and hasattr(type(result), "__get__"):
1984
return result.__get__(obj, cls)
1987
def _get_kind_attr(self, kind):
1988
if kind == "getattr":
1989
return "__getattribute__"
1990
return "__%s__" % kind
1993
for kind in self._monitored:
1994
attr = self._get_kind_attr(kind)
1996
for obj in self._monitored[kind].itervalues():
1998
if issubclass(cls, type):
2002
unpatched = getattr(cls, attr, Undefined)
2003
self.patch_attr(cls, attr,
2004
PatchedMethod(kind, unpatched,
2005
self.is_monitoring))
2006
self.patch_attr(cls, "__mocker_execute__",
2010
for obj, attr, original in self._patched.itervalues():
2011
if original is Undefined:
2014
setattr(obj, attr, original)
2015
self._patched.clear()
2017
def execute(self, action, object):
2018
attr = self._get_kind_attr(action.kind)
2019
unpatched = self.get_unpatched_attr(object, attr)
2021
return unpatched(*action.args, **action.kwargs)
2022
except AttributeError:
2023
type, value, traceback = sys.exc_info()
2024
if action.kind == "getattr":
2025
# The normal behavior of Python is to try __getattribute__,
2026
# and if it raises AttributeError, try __getattr__. We've
2027
# tried the unpatched __getattribute__ above, and we'll now
2030
__getattr__ = unpatched("__getattr__")
2031
except AttributeError:
2034
return __getattr__(*action.args, **action.kwargs)
2035
raise type, value, traceback
2038
class PatchedMethod(object):
2040
def __init__(self, kind, unpatched, is_monitoring):
2042
self._unpatched = unpatched
2043
self._is_monitoring = is_monitoring
2045
def __get__(self, obj, cls=None):
2047
if not self._is_monitoring(object, self._kind):
2048
return self._unpatched.__get__(obj, cls)
2049
def method(*args, **kwargs):
2050
if self._kind == "getattr" and args[0].startswith("__mocker_"):
2051
return self._unpatched.__get__(obj, cls)(args[0])
2052
mock = object.__mocker_mock__
2053
return mock.__mocker_act__(self._kind, args, kwargs, object)
2056
def __call__(self, obj, *args, **kwargs):
2057
# At least with __getattribute__, Python seems to use *both* the
2058
# descriptor API and also call the class attribute directly. It
2059
# looks like an interpreter bug, or at least an undocumented
2061
return self.__get__(obj)(*args, **kwargs)
2064
def patcher_recorder(mocker, event):
2065
mock = event.path.root_mock
2066
if mock.__mocker_patcher__ and len(event.path.actions) == 1:
2067
patcher = mock.__mocker_patcher__
2068
patcher.monitor(mock.__mocker_object__, event.path.actions[0].kind)
2070
Mocker.add_recorder(patcher_recorder)