20 """Mox, an object-mocking framework for Python.
22 Mox works in the record-replay-verify paradigm. When you first create
23 a mock object, it is in record mode. You then programmatically set
24 the expected behavior of the mock object (what methods are to be
25 called on it, with what parameters, what they should return, and in
28 Once you have set up the expected mock behavior, you put it in replay
29 mode. Now the mock responds to method calls just as you told it to.
30 If an unexpected method (or an expected method with unexpected
31 parameters) is called, then an exception will be raised.
33 Once you are done interacting with the mock, you need to verify that
34 all the expected interactions occurred. (Maybe your code exited
35 prematurely without calling some cleanup method!) The verify phase
36 ensures that every expected method was called; otherwise, an exception
39 Suggested usage / workflow:
44 # Create a mock data access object
45 mock_dao = my_mox.CreateMock(DAOClass)
47 # Set up expected behavior
48 mock_dao.RetrievePersonWithIdentifier('1').AndReturn(person)
49 mock_dao.DeletePerson(person)
51 # Put mocks in replay mode
54 # Inject mock object and run test
55 controller.SetDao(mock_dao)
56 controller.DeletePersonById('1')
58 # Verify all methods were called as expected
62 from collections
import deque
69 class Error(AssertionError):
70 """Base exception for this module."""
75 class ExpectedMethodCallsError(Error):
76 """Raised when Verify() is called before all expected methods have been called
83 # expected_methods: A sequence of MockMethod objects that should have been
85 expected_methods: [MockMethod]
88 ValueError: if expected_methods contains no methods.
91 if not expected_methods:
92 raise ValueError(
"There must be at least one expected method")
97 calls =
"\n".join([
"%3d. %s" % (i, m)
99 return "Verify: Expected methods never called:\n%s" % (calls,)
103 """Raised when an unexpected method is called.
105 This can occur if a method is called with incorrect parameters, or out of the
113 # unexpected_method: MockMethod that was called but was not at the head of
114 # the expected_method queue.
115 # expected: MockMethod or UnorderedGroup the method should have
117 unexpected_method: MockMethod
118 expected: MockMethod or UnorderedGroup
126 return "Unexpected method call: %s. Expecting: %s" % \
131 """Raised if an unknown method is requested of the mock object."""
137 # unknown_method_name: Method call that is not part of the mocked class's
139 unknown_method_name: str
146 return "Method called is not a member of the object: %s" % \
151 """Mox: a factory for creating mock objects."""
155 _USE_MOCK_OBJECT = [types.ClassType, types.InstanceType, types.ModuleType,
156 types.ObjectType, types.TypeType]
159 """Initialize a new Mox."""
165 """Create a new mock object.
168 # class_to_mock: the class to be mocked
172 MockObject that can be used as the class_to_mock would be.
180 """Create a mock that will accept any method calls.
182 This does not enforce an interface.
190 """Set all mock objects to replay mode."""
197 """Call verify on all mock objects created."""
203 """Call reset on all mock objects. This does not unset stubs."""
209 """Replace a method, attribute, etc. with a Mock.
211 This will replace a class or module with a MockObject, and everything else
212 (method, function, etc) with a MockAnything. This can be overridden to
213 always use a MockAnything by setting use_mock_anything to True.
216 obj: A Python object (class, module, instance, callable).
217 attr_name: str. The name of the attribute to replace with a mock.
218 use_mock_anything: bool. True if a MockAnything should be used regardless
219 of the type of attribute.
222 attr_to_replace = getattr(obj, attr_name)
228 self.
stubs.Set(obj, attr_name, stub)
231 """Restore stubs to their original state."""
233 self.
stubs.UnsetAll()
236 """Put mocks into Replay mode.
239 # args is any number of mocks to put into replay mode.
250 # args is any number of mocks to be verified.
261 # args is any number of mocks to be reset.
269 """A mock that can be used to mock anything.
271 This is helpful for mocking classes that do not provide a public interface.
279 """Intercept method calls on this object.
281 A new MockMethod is returned that is aware of the MockAnything's
282 state (record or replay). The call will be recorded or replayed
283 by the MockMethod's __call__.
286 # method name: the name of the method being called.
290 A new MockMethod aware of MockAnything's state (record or replay).
296 """Create a new mock method call and return it.
299 # method name: the name of the method being called.
303 A new MockMethod aware of MockAnything's state (record or replay).
310 """Return 1 for nonzero so the mock can be used as a conditional."""
315 """Provide custom logic to compare objects."""
317 return (isinstance(rhs, MockAnything)
and
322 """Provide custom logic to compare objects."""
324 return not self == rhs
327 """Start replaying expected method calls."""
332 """Verify that all of the expected calls have been made.
335 ExpectedMethodCallsError: if there are still more method calls in the
350 """Reset the state of this mock to record mode with an empty queue."""
360 """A mock object that simulates the public/protected interface of a class."""
363 """Initialize a mock object.
365 This determines the methods and properties of the class and stores them.
368 # class_to_mock: class to be mocked
374 MockAnything.__dict__[
'__init__'](self)
380 for method
in dir(class_to_mock):
381 if callable(getattr(class_to_mock, method)):
387 """Intercept attribute request on this object.
389 If the attribute is a public class variable, it will be returned and not
392 If the attribute is not a variable, it is handled like a method
393 call. The method name is checked against the set of mockable
394 methods, and a new MockMethod is returned that is aware of the
395 MockObject's state (record or replay). The call will be recorded
396 or replayed by the MockMethod's __call__.
399 # name: the name of the attribute being requested.
403 Either a class variable or a new MockMethod that is aware of the state
404 of the mock (record or replay).
407 UnknownMethodCallError if the MockObject does not mock the requested
420 """Provide custom logic to compare objects."""
422 return (isinstance(rhs, MockObject)
and
428 """Provide custom logic for mocking classes that support item assignment.
431 key: Key to set the value for.
435 Expected return value in replay mode. A MockMethod object for the
436 __setitem__ method that has already been called if not in replay mode.
439 TypeError if the underlying class does not support item assignment.
440 UnexpectedMethodCallError if the object does not expect the call to
448 raise TypeError(
'object does not support item assignment')
460 """Provide custom logic for mocking classes that are subscriptable.
463 key: Key to return the value for.
466 Expected return value in replay mode. A MockMethod object for the
467 __getitem__ method that has already been called if not in replay mode.
470 TypeError if the underlying class is not subscriptable.
471 UnexpectedMethodCallError if the object does not expect the call to
479 raise TypeError(
'unsubscriptable object')
491 """Provide custom logic for mocking classes that are callable."""
496 raise TypeError(
'Not callable')
501 return mock_method(*params, **named_params)
505 """Return the class that is being mocked."""
511 """Callable mock method.
513 A MockMethod should act exactly like the method it mocks, accepting parameters
514 and returning a value, or throwing an exception (as specified). When this
515 method is called, it can optionally verify whether the called method (name and
516 signature) matches the expected method.
519 def __init__(self, method_name, call_queue, replay_mode):
520 """Construct a new mock method.
523 # method_name: the name of the method
524 # call_queue: deque of calls, verify this call against the head, or add
525 # this call to the queue.
526 # replay_mode: False if we are recording, True if we are verifying calls
527 # against the call queue.
529 call_queue: list or deque
533 self.
_name = method_name
535 if not isinstance(call_queue, deque):
546 """Log parameters and return the specified return value.
548 If the Mock(Anything/Object) associated with this call is in record mode,
549 this MockMethod will be pushed onto the expected call queue. If the mock
550 is in replay mode, this will pop a MockMethod off the top of the queue and
551 verify this call is equal to the expected call.
554 UnexpectedMethodCall if this call is supposed to match an expected method
555 call and it does not.
567 if expected_method._side_effects:
568 expected_method._side_effects(*params, **named_params)
570 if expected_method._exception:
571 raise expected_method._exception
573 return expected_method._return_value
576 """Raise an AttributeError with a helpful message."""
578 raise AttributeError(
'MockMethod has no attribute "%s". '
579 'Did you remember to put your mocks in replay mode?' % name)
582 """Pop the next method from our call queue."""
589 """Verify the called method is expected.
591 This can be an ordered method, or part of an unordered set.
594 The expected mock method.
597 UnexpectedMethodCall if the method called was not expected.
604 while isinstance(expected, MethodGroup):
605 expected, method = expected.MethodCalled(self)
606 if method
is not None:
617 [repr(p)
for p
in self.
_params or []] +
623 """Test whether this MockMethod is equivalent to another MockMethod.
626 # rhs: the right hand side of the test
630 return (isinstance(rhs, MockMethod)
and
631 self.
_name == rhs._name
and
632 self.
_params == rhs._params
and
636 """Test whether this MockMethod is not equivalent to another MockMethod.
639 # rhs: the right hand side of the test
643 return not self == rhs
646 """Returns a possible group from the end of the call queue or None if no
647 other methods are on the stack.
652 assert this_method == self
665 """Checks if the last method (a possible group) is an instance of our
666 group_class. Adds the current method to this group or creates a new one.
670 group_name: the name of the group.
671 group_class: the class used to create instance of this new group
676 if isinstance(group, group_class)
and group.group_name() == group_name:
677 group.AddMethod(self)
681 new_group = group_class(group_name)
682 new_group.AddMethod(self)
687 """Move this method into a group of unordered calls.
689 A group of unordered calls must be defined together, and must be executed
690 in full before the next expected method can be called. There can be
691 multiple groups that are expected serially, if they are given
692 different group names. The same group name can be reused if there is a
693 standard method call, or a group with a different name, spliced between
697 group_name: the name of the unordered group.
705 """Move this method into group of calls which may be called multiple times.
707 A group of repeating calls must be defined together, and must be executed in
708 full before the next expected method can be called.
711 group_name: the name of the unordered group.
719 """Set the value to return when this method is called.
722 # return_value can be anything.
729 """Set the exception to raise when this method is called.
732 # exception: the exception to raise when this method is called.
739 """Set the side effects that are simulated when this method is called.
742 side_effects: A callable which modifies the parameters or other relevant
743 state which a given test case depends on.
746 Self for chaining with AndReturn and AndRaise.
752 """Base class for all Mox comparators.
754 A Comparator can be used as a parameter to a mocked method when the exact
755 value is not known. For example, the code you are testing might build up a
756 long SQL string that is passed to your mock DAO. You're only interested that
757 the IN clause contains the proper primary keys, so you can set your mock
760 mock_dao.RunQuery(StrContains('IN (1, 2, 4, 5)')).AndReturn(mock_result)
762 Now whatever query is passed in must contain the string 'IN (1, 2, 4, 5)'.
764 A Comparator may replace one or more parameters, for example:
765 # return at most 10 rows
766 mock_dao.RunQuery(StrContains('SELECT'), 10)
770 # Return some non-deterministic number of rows
771 mock_dao.RunQuery(StrContains('SELECT'), IsA(int))
775 """Special equals method that all comparators must implement.
778 rhs: any python object
781 raise NotImplementedError(
'method must be implemented by a subclass.')
787 return not self.
equals(rhs)
791 """This class wraps a basic Python type or class. It is used to verify
792 that a parameter is of the given type or class.
795 mock_dao.Connect(IsA(DbConnectInfo))
802 class_name: basic python type or a class
808 """Check to see if the RHS is an instance of class_name.
811 # rhs: the right hand side of the test
829 """Comparison class used to check whether a parameter is nearly equal
830 to a given value. Generally useful for floating point numbers.
832 Example mock_dao.SetTimeout((IsAlmost(3.9)))
836 """Initialize IsAlmost.
839 float_value: The value for making the comparison.
840 places: The number of decimal places to round to.
847 """Check to see if RHS is almost equal to float_value
850 rhs: the value to compare to float_value
866 """Comparison class used to check whether a substring exists in a
867 string parameter. This can be useful in mocking a database with SQL
868 passed in as a string parameter, for example.
871 mock_dao.RunQuery(StrContains('IN (1, 2, 4, 5)')).AndReturn(mock_result)
878 # search_string: the string you are searching for
885 """Check to see if the search_string is contained in the rhs string.
888 # rhs: the right hand side of the test
905 """Checks if a string matches a regular expression.
907 This uses a given regular expression to determine equality.
914 # pattern is the regular expression to search for
916 # flags passed to re.compile function as the second argument
920 self.
regex = re.compile(pattern, flags=flags)
923 """Check to see if rhs matches regular expression pattern.
932 s =
'<regular expression \'%s\'' % self.
regex.pattern
934 s +=
', flags=%d' % self.
regex.flags
940 """Checks whether an item (or key) is in a list (or dict) parameter.
943 mock_dao.GetUsersInfo(In('expectedUserName')).AndReturn(mock_result)
950 # key is any thing that could be in a list or a key in a dict
956 """Check to see whether key is in rhs.
965 return self.
_key in rhs
968 return '<sequence or map containing \'%s\'>' % self.
_key
972 """Checks whether a key/value pair is in a dict parameter.
975 mock_dao.UpdateUsers(ContainsKeyValue('stevepm', stevepm_user_info))
982 # key: a key in a dict
983 # value: the corresponding value
990 """Check whether the given key/value pair is in the rhs dict.
1002 return '<map containing the entry \'%s: %s\'>' % (self.
_key, self.
_value)
1006 """Checks whether iterables contain the same elements (ignoring order).
1009 mock_dao.ProcessUsers(SameElementsAs('stevepm', 'salomaki'))
1016 expected_seq: a sequence
1022 """Check to see whether actual_seq has same elements as expected_seq.
1025 actual_seq: sequence
1032 expected = dict([(element,
None)
for element
in self.
_expected_seq])
1033 actual = dict([(element,
None)
for element
in actual_seq])
1037 actual = list(actual_seq)
1040 return expected == actual
1043 return '<sequence with same elements as \'%s\'>' % self.
_expected_seq
1047 """Evaluates one or more Comparators on RHS and returns an AND of the results.
1054 *args: One or more Comparator
1060 """Checks whether all Comparators are equal to rhs.
1063 # rhs: can be anything
1070 if not comparator.equals(rhs):
1080 """Evaluates one or more Comparators on RHS and returns an OR of the results.
1087 *args: One or more Mox comparators
1093 """Checks whether any Comparator is equal to rhs.
1096 # rhs: can be anything
1103 if comparator.equals(rhs):
1113 """Call a function that should verify the parameter passed in is correct.
1115 You may need the ability to perform more advanced operations on the parameter
1116 in order to validate it. You can use this to have a callable validate any
1117 parameter. The callable should return either True or False.
1122 def myParamValidator(param):
1123 # Advanced logic here
1126 mock_dao.DoSomething(Func(myParamValidator), true)
1133 func: callable that takes one parameter and returns a bool
1139 """Test whether rhs passes the function test.
1141 rhs is passed into func.
1144 rhs: any python object
1147 the result of func(rhs)
1150 return self.
_func(rhs)
1157 """Ignore an argument.
1159 This can be used when we don't care about an argument of a method call.
1162 # Check if CastMagic is called with 3 as first arg and 'disappear' as third.
1163 mymock.CastMagic(3, IgnoreArg(), 'disappear')
1167 """Ignores arguments and returns True.
1170 unused_rhs: any python object
1179 return '<IgnoreArg>'
1183 """Base class containing common behaviour for MethodGroups."""
1192 return '<%s "%s">' % (self.__class__.__name__, self.
_group_name)
1195 raise NotImplementedError
1198 raise NotImplementedError
1201 raise NotImplementedError
1204 """UnorderedGroup holds a set of method calls that may occur in any order.
1206 This construct is helpful for non-deterministic events, such as iterating
1207 over the keys of a dict.
1211 super(UnorderedGroup, self).
__init__(group_name)
1215 """Add a method to this group.
1218 mock_method: A mock method to be added to this group.
1224 """Remove a method call from the group.
1226 If the method is not in the set, an UnexpectedMethodCallError will be
1230 mock_method: a mock method that should be equal to a method in the group.
1233 The mock method from the group
1236 UnexpectedMethodCallError if the mock_method was not in the group.
1242 if method == mock_method:
1251 mock_method._call_queue.appendleft(self)
1258 """Return True if there are not any methods in this group."""
1264 """MultipleTimesGroup holds methods that may be called any number of times.
1266 Note: Each method must be called at least once.
1268 This is helpful, if you don't know or care how many times a method is called.
1272 super(MultipleTimesGroup, self).
__init__(group_name)
1277 """Add a method to this group.
1280 mock_method: A mock method to be added to this group.
1286 """Remove a method call from the group.
1288 If the method is not in the set, an UnexpectedMethodCallError will be
1292 mock_method: a mock method that should be equal to a method in the group.
1295 The mock method from the group
1298 UnexpectedMethodCallError if the mock_method was not in the group.
1305 if method == mock_method:
1309 mock_method._call_queue.appendleft(self)
1313 next_method = mock_method._PopNextMethod();
1314 return next_method,
None
1319 """Return True if all methods in this group are called at least once."""
1325 for expected
in tmp:
1326 if called == expected:
1327 tmp.remove(expected)
1335 """Metaclass to add mox cleanup and verification to every test.
1337 As the mox unit testing class is being constructed (MoxTestBase or a
1338 subclass), this metaclass will modify all test functions to call the
1339 CleanUpMox method of the test class after they finish. This means that
1340 unstubbing and verifying will happen for every test with no additional code,
1341 and any failures will result in test failures as opposed to errors.
1345 type.__init__(cls, name, bases, d)
1350 for attr_name
in dir(base):
1351 d[attr_name] = getattr(base, attr_name)
1353 for func_name, func
in d.items():
1354 if func_name.startswith(
'test')
and callable(func):
1355 setattr(cls, func_name, MoxMetaTestBase.CleanUpTest(cls, func))
1359 """Adds Mox cleanup code to any MoxTestBase method.
1361 Always unsets stubs after a test. Will verify all mocks for tests that
1365 cls: MoxTestBase or subclass; the class whose test method we are altering.
1366 func: method; the method of the MoxTestBase test class we wish to alter.
1369 The modified method.
1371 def new_method(self, *args, **kwargs):
1372 mox_obj = getattr(self,
'mox',
None)
1374 if mox_obj
and isinstance(mox_obj, Mox):
1377 func(self, *args, **kwargs)
1380 mox_obj.UnsetStubs()
1383 new_method.__name__ = func.__name__
1384 new_method.__doc__ = func.__doc__
1385 new_method.__module__ = func.__module__
1390 """Convenience test class to make stubbing easier.
1392 Sets up a "mox" attribute which is an instance of Mox - any mox tests will
1393 want this. Also automatically unsets any stubs and verifies that all mock
1394 methods have been called at the end of each test, eliminating boilerplate
1398 __metaclass__ = MoxMetaTestBase