diff --git a/Doc/library/unittest.mock-examples.rst b/Doc/library/unittest.mock-examples.rst --- a/Doc/library/unittest.mock-examples.rst +++ b/Doc/library/unittest.mock-examples.rst @@ -751,26 +751,26 @@ defined in 'mymodule':: "First frob and then clear val" frob(val) val.clear() When we try to test that ``grob`` calls ``frob`` with the correct argument look what happens: >>> with patch('mymodule.frob') as mock_frob: - ... val = set([6]) + ... val = {6} ... mymodule.grob(val) ... >>> val - set([]) - >>> mock_frob.assert_called_with(set([6])) + set() + >>> mock_frob.assert_called_with({6}) Traceback (most recent call last): ... - AssertionError: Expected: ((set([6]),), {}) - Called with: ((set([]),), {}) + AssertionError: Expected: (({6},), {}) + Called with: ((set(),), {}) One possibility would be for mock to copy the arguments you pass in. This could then cause problems if you do assertions that rely on object identity for equality. Here's one solution that uses the :attr:`side_effect` functionality. If you provide a ``side_effect`` function for a mock then ``side_effect`` will be called with the same args as the mock. This gives us an @@ -788,38 +788,38 @@ me. ... kwargs = deepcopy(kwargs) ... new_mock(*args, **kwargs) ... return DEFAULT ... mock.side_effect = side_effect ... return new_mock ... >>> with patch('mymodule.frob') as mock_frob: ... new_mock = copy_call_args(mock_frob) - ... val = set([6]) + ... val = {6} ... mymodule.grob(val) ... - >>> new_mock.assert_called_with(set([6])) + >>> new_mock.assert_called_with({6}) >>> new_mock.call_args - call(set([6])) + call({6}) ``copy_call_args`` is called with the mock that will be called. It returns a new mock that we do the assertion on. The ``side_effect`` function makes a copy of the args and calls our ``new_mock`` with the copy. .. note:: If your mock is only going to be used once there is an easier way of checking arguments at the point they are called. You can simply do the checking inside a ``side_effect`` function. >>> def side_effect(arg): - ... assert arg == set([6]) + ... assert arg == {6} ... >>> mock = Mock(side_effect=side_effect) - >>> mock(set([6])) + >>> mock({6}) >>> mock(set()) Traceback (most recent call last): ... AssertionError An alternative approach is to create a subclass of :class:`Mock` or :class:`MagicMock` that copies (using :func:`copy.deepcopy`) the arguments. Here's an example implementation: @@ -834,18 +834,18 @@ Here's an example implementation: >>> c = CopyingMock(return_value=None) >>> arg = set() >>> c(arg) >>> arg.add(1) >>> c.assert_called_with(set()) >>> c.assert_called_with(arg) Traceback (most recent call last): ... - AssertionError: Expected call: mock(set([1])) - Actual call: mock(set([])) + AssertionError: Expected call: mock({1}) + Actual call: mock(set()) >>> c.foo When you subclass ``Mock`` or ``MagicMock`` all dynamically created attributes, and the ``return_value`` will use your subclass automatically. That means all children of a ``CopyingMock`` will also have the type ``CopyingMock``. diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -270,23 +270,21 @@ DEFAULT = sentinel.DEFAULT def _copy(value): if type(value) in (dict, list, tuple, set): return type(value)(value) return value -_allowed_names = set( - [ - 'return_value', '_mock_return_value', 'side_effect', - '_mock_side_effect', '_mock_parent', '_mock_new_parent', - '_mock_name', '_mock_new_name' - ] -) +_allowed_names = { + 'return_value', '_mock_return_value', 'side_effect', + '_mock_side_effect', '_mock_parent', '_mock_new_parent', + '_mock_name', '_mock_new_name' +} def _delegating_property(name): _allowed_names.add(name) _the_name = '_mock_' + name def _get(self, name=name, _the_name=_the_name): sig = self._mock_delegate if sig is None: @@ -1674,44 +1672,45 @@ numerics = ( ) inplace = ' '.join('i%s' % n for n in numerics.split()) right = ' '.join('r%s' % n for n in numerics.split()) # not including __prepare__, __instancecheck__, __subclasscheck__ # (as they are metaclass methods) # __del__ is not supported at all as it causes problems if it exists -_non_defaults = set('__%s__' % method for method in [ - 'get', 'set', 'delete', 'reversed', 'missing', 'reduce', 'reduce_ex', - 'getinitargs', 'getnewargs', 'getstate', 'setstate', 'getformat', - 'setformat', 'repr', 'dir', 'subclasses', 'format', -]) +_non_defaults = { + '__get__', '__set__', '__delete__', '__reversed__', '__missing__', + '__reduce__', '__reduce_ex__', '__getinitargs__', '__getnewargs__', + '__getstate__', '__setstate__', '__getformat__', '__setformat__', + '__repr__', '__dir__', '__subclasses__', '__format__', +) def _get_method(name, func): "Turns a callable object (like a mock) into a real function" def method(self, *args, **kw): return func(self, *args, **kw) method.__name__ = name return method -_magics = set( +_magics = { '__%s__' % method for method in ' '.join([magic_methods, numerics, inplace, right]).split() -) +} _all_magics = _magics | _non_defaults -_unsupported_magics = set([ +_unsupported_magics = { '__getattr__', '__setattr__', '__init__', '__new__', '__prepare__' '__instancecheck__', '__subclasscheck__', '__del__' -]) +} _calculate_return_value = { '__hash__': lambda self: object.__hash__(self), '__str__': lambda self: object.__str__(self), '__sizeof__': lambda self: object.__sizeof__(self), } _return_values = {