call
¶
-
unittest.mock.
call
(
*
args
,
**
kwargs
)
¶
-
call()
is a helper object for making simpler assertions, for comparing with
call_args
,
call_args_list
,
mock_calls
and
method_calls
.
call()
can also be used with
assert_has_calls()
.
>>> m = MagicMock(return_value=None)
>>> m(1, 2, a='foo', b='bar')
>>> m()
>>> m.call_args_list == [call(1, 2, a='foo', b='bar'), call()]
True
-
调用。
call_list
(
)
¶
-
For a call object that represents multiple calls,
call_list()
returns a list of all the intermediate calls as well as the final call.
call_list
is particularly useful for making assertions on “chained calls”. A chained call is multiple calls on a single line of code. This results in multiple entries in
mock_calls
on a mock. Manually constructing the sequence of calls can be tedious.
call_list()
can construct the sequence of calls from the same chained call:
>>> m = MagicMock()
>>> m(1).method(arg='foo').other('bar')(2.0)
<MagicMock name='mock().method().other()()' id='...'>
>>> kall = call(1).method(arg='foo').other('bar')(2.0)
>>> kall.call_list()
[call(1),
call().method(arg='foo'),
call().method().other('bar'),
call().method().other()(2.0)]
>>> m.mock_calls == kall.call_list()
True
A
call
object is either a tuple of (positional args, keyword args) or (name, positional args, keyword args) depending on how it was constructed. When you construct them yourself this isn’t particularly interesting, but the
call
objects that are in the
Mock.call_args
,
Mock.call_args_list
and
Mock.mock_calls
attributes can be introspected to get at the individual arguments they contain.
The
call
对象在
Mock.call_args
and
Mock.call_args_list
are two-tuples of (positional args, keyword args) whereas the
call
对象在
Mock.mock_calls
, along with ones you construct yourself, are three-tuples of (name, positional args, keyword args).
You can use their “tupleness” to pull out the individual arguments for more complex introspection and assertions. The positional arguments are a tuple (an empty tuple if there are no positional arguments) and the keyword arguments are a dictionary:
>>> m = MagicMock(return_value=None)
>>> m(1, 2, 3, arg='one', arg2='two')
>>> kall = m.call_args
>>> kall.args
(1, 2, 3)
>>> kall.kwargs
{'arg': 'one', 'arg2': 'two'}
>>> kall.args is kall[0]
True
>>> kall.kwargs is kall[1]
True
>>> m = MagicMock()
>>> m.foo(4, 5, 6, arg='two', arg2='three')
<MagicMock name='mock.foo()' id='...'>
>>> kall = m.mock_calls[0]
>>> name, args, kwargs = kall
>>> name
'foo'
>>> args
(4, 5, 6)
>>> kwargs
{'arg': 'two', 'arg2': 'three'}
>>> name is m.mock_calls[0][0]
True
FILTER_DIR
¶
-
unittest.mock.
FILTER_DIR
¶
FILTER_DIR
is a module level variable that controls the way mock objects respond to
dir()
。默认为
True
, which uses the filtering described below, to only show useful members. If you dislike this filtering, or need to switch it off for diagnostic purposes, then set
mock.FILTER_DIR = False
.
With filtering on,
dir(some_mock)
shows only useful attributes and will include any dynamically created attributes that wouldn’t normally be shown. If the mock was created with a
spec
(或
autospec
of course) then all the attributes from the original are shown, even if they haven’t been accessed yet:
>>> dir(Mock())
['assert_any_call',
'assert_called',
'assert_called_once',
'assert_called_once_with',
'assert_called_with',
'assert_has_calls',
'assert_not_called',
'attach_mock',
...
>>> from urllib import request
>>> dir(Mock(spec=request))
['AbstractBasicAuthHandler',
'AbstractDigestAuthHandler',
'AbstractHTTPHandler',
'BaseHandler',
...
Many of the not-very-useful (private to
Mock
rather than the thing being mocked) underscore and double underscore prefixed attributes have been filtered from the result of calling
dir()
在
Mock
. If you dislike this behaviour you can switch it off by setting the module level switch
FILTER_DIR
:
>>> from unittest import mock
>>> mock.FILTER_DIR = False
>>> dir(mock.Mock())
['_NonCallableMock__get_return_value',
'_NonCallableMock__get_side_effect',
'_NonCallableMock__return_value_doc',
'_NonCallableMock__set_return_value',
'_NonCallableMock__set_side_effect',
'__call__',
'__class__',
...
Alternatively you can just use
vars(my_mock)
(instance members) and
dir(type(my_mock))
(type members) to bypass the filtering irrespective of
FILTER_DIR
.
mock_open
¶
-
unittest.mock.
mock_open
(
mock
=
None
,
read_data
=
None
)
¶
-
A helper function to create a mock to replace the use of
open()
. It works for
open()
called directly or used as a context manager.
The
mock
argument is the mock object to configure. If
None
(the default) then a
MagicMock
will be created for you, with the API limited to methods or attributes available on standard file handles.
read_data
is a string for the
read()
,
readline()
,和
readlines()
methods of the file handle to return. Calls to those methods will take data from
read_data
until it is depleted. The mock of these methods is pretty simplistic: every time the
mock
is called, the
read_data
is rewound to the start. If you need more control over the data that you are feeding to the tested code you will need to customize this mock for yourself. When that is insufficient, one of the in-memory filesystem packages on
PyPI
can offer a realistic filesystem for testing.
3.4 版改变:
添加
readline()
and
readlines()
support. The mock of
read()
changed to consume
read_data
rather than returning it on each call.
3.5 版改变:
read_data
is now reset on each call to the
mock
.
3.8 版改变:
添加
__iter__()
to implementation so that iteration (such as in for loops) correctly consumes
read_data
.
使用
open()
as a context manager is a great way to ensure your file handles are closed properly and is becoming common:
with open('/some/path', 'w') as f:
f.write('something')
The issue is that even if you mock out the call to
open()
it is the
returned object
that is used as a context manager (and has
__enter__()
and
__exit__()
called).
Mocking context managers with a
MagicMock
is common enough and fiddly enough that a helper function is useful.
>>> m = mock_open()
>>> with patch('__main__.open', m):
... with open('foo', 'w') as h:
... h.write('some stuff')
...
>>> m.mock_calls
[call('foo', 'w'),
call().__enter__(),
call().write('some stuff'),
call().__exit__(None, None, None)]
>>> m.assert_called_once_with('foo', 'w')
>>> handle = m()
>>> handle.write.assert_called_once_with('some stuff')
And for reading files:
>>> with patch('__main__.open', mock_open(read_data='bibble')) as m:
... with open('foo') as h:
... result = h.read()
...
>>> m.assert_called_once_with('foo')
>>> assert result == 'bibble'
Autospeccing
¶
Autospeccing is based on the existing
spec
feature of mock. It limits the api of mocks to the api of an original object (the spec), but it is recursive (implemented lazily) so that attributes of mocks only have the same api as the attributes of the spec. In addition mocked functions / methods have the same call signature as the original so they raise a
TypeError
if they are called incorrectly.
Before I explain how auto-speccing works, here’s why it is needed.
Mock
is a very powerful and flexible object, but it suffers from a flaw which is general to mocking. If you refactor some of your code, rename members and so on, any tests for code that is still using the
old api
but uses mocks instead of the real objects will still pass. This means your tests can all pass even though your code is broken.
3.5 版改变:
Before 3.5, tests with a typo in the word assert would silently pass when they should raise an error. You can still achieve this behavior by passing
unsafe=True
to Mock.
Note that this is another reason why you need integration tests as well as unit tests. Testing everything in isolation is all fine and dandy, but if you don’t test how your units are “wired together” there is still lots of room for bugs that tests might have caught.
unittest.mock
already provides a feature to help with this, called speccing. If you use a class or instance as the
spec
for a mock then you can only access attributes on the mock that exist on the real class:
>>> from urllib import request
>>> mock = Mock(spec=request.Request)
>>> mock.assret_called_with # Intentional typo!
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
The spec only applies to the mock itself, so we still have the same issue with any methods on the mock:
>>> mock.has_data()
<mock.Mock object at 0x...>
>>> mock.has_data.assret_called_with() # Intentional typo!
Auto-speccing solves this problem. You can either pass
autospec=True
to
patch()
/
patch.object()
或使用
create_autospec()
function to create a mock with a spec. If you use the
autospec=True
自变量对于
patch()
then the object that is being replaced will be used as the spec object. Because the speccing is done “lazily” (the spec is created as attributes on the mock are accessed) you can use it with very complex or deeply nested objects (like modules that import modules that import modules) without a big performance hit.
Here’s an example of it in use:
>>> from urllib import request
>>> patcher = patch('__main__.request', autospec=True)
>>> mock_request = patcher.start()
>>> request is mock_request
True
>>> mock_request.Request
<MagicMock name='request.Request' spec='Request' id='...'>
You can see that
request.Request
has a spec.
request.Request
takes two arguments in the constructor (one of which is
self
). Here’s what happens if we try to call it incorrectly:
>>> req = request.Request()
Traceback (most recent call last):
...
TypeError: <lambda>() takes at least 2 arguments (1 given)
The spec also applies to instantiated classes (i.e. the return value of specced mocks):
>>> req = request.Request('foo')
>>> req
<NonCallableMagicMock name='request.Request()' spec='Request' id='...'>
Request
objects are not callable, so the return value of instantiating our mocked out
request.Request
is a non-callable mock. With the spec in place any typos in our asserts will raise the correct error:
>>> req.add_header('spam', 'eggs')
<MagicMock name='request.Request().add_header()' id='...'>
>>> req.add_header.assret_called_with # Intentional typo!
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'assret_called_with'
>>> req.add_header.assert_called_with('spam', 'eggs')
In many cases you will just be able to add
autospec=True
to your existing
patch()
calls and then be protected against bugs due to typos and api changes.
As well as using
autospec
through
patch()
there is a
create_autospec()
for creating autospecced mocks directly:
>>> from urllib import request
>>> mock_request = create_autospec(request)
>>> mock_request.Request('foo', 'bar')
<NonCallableMagicMock name='mock.Request()' spec='Request' id='...'>
This isn’t without caveats and limitations however, which is why it is not the default behaviour. In order to know what attributes are available on the spec object, autospec has to introspect (access attributes) the spec. As you traverse attributes on the mock a corresponding traversal of the original object is happening under the hood. If any of your specced objects have properties or descriptors that can trigger code execution then you may not be able to use autospec. On the other hand it is much better to design your objects so that introspection is safe
.
A more serious problem is that it is common for instance attributes to be created in the
__init__()
method and not to exist on the class at all.
autospec
can’t know about any dynamically created attributes and restricts the api to visible attributes.
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
There are a few different ways of resolving this problem. The easiest, but not necessarily the least annoying, way is to simply set the required attributes on the mock after creation. Just because
autospec
doesn’t allow you to fetch attributes that don’t exist on the spec it doesn’t prevent you setting them:
>>> with patch('__main__.Something', autospec=True):
... thing = Something()
... thing.a = 33
...
There is a more aggressive version of both
spec
and
autospec
that
does
prevent you setting non-existent attributes. This is useful if you want to ensure your code only
sets
valid attributes too, but obviously it prevents this particular scenario:
>>> with patch('__main__.Something', autospec=True, spec_set=True):
... thing = Something()
... thing.a = 33
...
Traceback (most recent call last):
...
AttributeError: Mock object has no attribute 'a'
Probably the best way of solving the problem is to add class attributes as default values for instance members initialised in
__init__()
. Note that if you are only setting default attributes in
__init__()
then providing them via class attributes (shared between instances of course) is faster too. e.g.
This brings up another issue. It is relatively common to provide a default value of
None
for members that will later be an object of a different type.
None
would be useless as a spec because it wouldn’t let you access
any
attributes or methods on it. As
None
is
never
going to be useful as a spec, and probably indicates a member that will normally of some other type, autospec doesn’t use a spec for members that are set to
None
. These will just be ordinary mocks (well - MagicMocks):
>>> class Something:
... member = None
...
>>> mock = create_autospec(Something)
>>> mock.member.foo.bar.baz()
<MagicMock name='mock.member.foo.bar.baz()' id='...'>
If modifying your production classes to add defaults isn’t to your liking then there are more options. One of these is simply to use an instance as the spec rather than the class. The other is to create a subclass of the production class and add the defaults to the subclass without affecting the production class. Both of these require you to use an alternative object as the spec. Thankfully
patch()
supports this - you can simply pass the alternative object as the
autospec
自变量:
>>> class Something:
... def __init__(self):
... self.a = 33
...
>>> class SomethingForTest(Something):
... a = 33
...
>>> p = patch('__main__.Something', autospec=SomethingForTest)
>>> mock = p.start()
>>> mock.a
<NonCallableMagicMock name='Something.a' spec='int' id='...'>
Order of precedence of
side_effect
,
return_value
and
wraps
¶
The order of their precedence is:
-
side_effect
-
return_value
-
wraps
If all three are set, mock will return the value from
side_effect
, ignoring
return_value
and the wrapped object altogether. If any two are set, the one with the higher precedence will return the value. Regardless of the order of which was set first, the order of precedence remains unchanged.
>>> from unittest.mock import Mock
>>> class Order:
... @staticmethod
... def get_value():
... return "third"
...
>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.get_value.side_effect = ["first"]
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'first'
As
None
is the default value of
side_effect
, if you reassign its value back to
None
, the order of precedence will be checked between
return_value
and the wrapped object, ignoring
side_effect
.
>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'
If the value being returned by
side_effect
is
DEFAULT
, it is ignored and the order of precedence moves to the successor to obtain the value to return.
>>> from unittest.mock import DEFAULT
>>> order_mock.get_value.side_effect = [DEFAULT]
>>> order_mock.get_value()
'second'
当
Mock
wraps an object, the default value of
return_value
将是
DEFAULT
.
>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.return_value
sentinel.DEFAULT
>>> order_mock.get_value.return_value
sentinel.DEFAULT
The order of precedence will ignore this value and it will move to the last successor which is the wrapped object.
As the real call is being made to the wrapped object, creating an instance of this mock will return the real instance of the class. The positional arguments, if any, required by the wrapped object must be passed.
>>> order_mock_instance = order_mock()
>>> isinstance(order_mock_instance, Order)
True
>>> order_mock_instance.get_value()
'third'
>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'second'
But if you assign
None
to it, this will not be ignored as it is an explicit assignment. So, the order of precedence will not move to the wrapped object.
>>> order_mock.get_value.return_value = None
>>> order_mock.get_value() is None
True
Even if you set all three at once when initializing the mock, the order of precedence remains the same:
>>> order_mock = Mock(spec=Order, wraps=Order,
... **{"get_value.side_effect": ["first"],
... "get_value.return_value": "second"}
... )
...
>>> order_mock.get_value()
'first'
>>> order_mock.get_value.side_effect = None
>>> order_mock.get_value()
'second'
>>> order_mock.get_value.return_value = DEFAULT
>>> order_mock.get_value()
'third'
若
side_effect
is exhausted, the order of precedence will not cause a value to be obtained from the successors. Instead,
StopIteration
异常被引发。
>>> order_mock = Mock(spec=Order, wraps=Order)
>>> order_mock.get_value.side_effect = ["first side effect value",
... "another side effect value"]
>>> order_mock.get_value.return_value = "second"
>>> order_mock.get_value()
'first side effect value'
>>> order_mock.get_value()
'another side effect value'
>>> order_mock.get_value()
Traceback (most recent call last):
...
StopIteration