mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 14:15:30 +00:00
584 lines
19 KiB
Plaintext
584 lines
19 KiB
Plaintext
=========
|
|
Helpers
|
|
=========
|
|
|
|
.. currentmodule:: mock
|
|
|
|
.. testsetup::
|
|
|
|
mock.FILTER_DIR = True
|
|
from pprint import pprint as pp
|
|
original_dir = dir
|
|
def dir(obj):
|
|
print pp(original_dir(obj))
|
|
|
|
import urllib2
|
|
__main__.urllib2 = urllib2
|
|
|
|
.. testcleanup::
|
|
|
|
dir = original_dir
|
|
mock.FILTER_DIR = True
|
|
|
|
|
|
|
|
call
|
|
====
|
|
|
|
.. function:: call(*args, **kwargs)
|
|
|
|
`call` is a helper object for making simpler assertions, for comparing
|
|
with :attr:`~Mock.call_args`, :attr:`~Mock.call_args_list`,
|
|
:attr:`~Mock.mock_calls` and :attr: `~Mock.method_calls`. `call` can also be
|
|
used with :meth:`~Mock.assert_has_calls`.
|
|
|
|
.. doctest::
|
|
|
|
>>> 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
|
|
|
|
.. method:: call.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 :attr:`~Mock.mock_calls` on a mock. Manually constructing
|
|
the sequence of calls can be tedious.
|
|
|
|
:meth:`~call.call_list` can construct the sequence of calls from the same
|
|
chained call:
|
|
|
|
.. doctest::
|
|
|
|
>>> 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
|
|
|
|
.. _calls-as-tuples:
|
|
|
|
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 :attr:`Mock.call_args`, :attr:`Mock.call_args_list` and
|
|
:attr:`Mock.mock_calls` attributes can be introspected to get at the individual
|
|
arguments they contain.
|
|
|
|
The `call` objects in :attr:`Mock.call_args` and :attr:`Mock.call_args_list`
|
|
are two-tuples of (positional args, keyword args) whereas the `call` objects
|
|
in :attr:`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:
|
|
|
|
.. doctest::
|
|
|
|
>>> m = MagicMock(return_value=None)
|
|
>>> m(1, 2, 3, arg='one', arg2='two')
|
|
>>> kall = m.call_args
|
|
>>> args, kwargs = kall
|
|
>>> args
|
|
(1, 2, 3)
|
|
>>> kwargs
|
|
{'arg2': 'two', 'arg': 'one'}
|
|
>>> args is kall[0]
|
|
True
|
|
>>> 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
|
|
{'arg2': 'three', 'arg': 'two'}
|
|
>>> name is m.mock_calls[0][0]
|
|
True
|
|
|
|
|
|
create_autospec
|
|
===============
|
|
|
|
.. function:: create_autospec(spec, spec_set=False, instance=False, **kwargs)
|
|
|
|
Create a mock object using another object as a spec. Attributes on the
|
|
mock will use the corresponding attribute on the `spec` object as their
|
|
spec.
|
|
|
|
Functions or methods being mocked will have their arguments checked to
|
|
ensure that they are called with the correct signature.
|
|
|
|
If `spec_set` is `True` then attempting to set attributes that don't exist
|
|
on the spec object will raise an `AttributeError`.
|
|
|
|
If a class is used as a spec then the return value of the mock (the
|
|
instance of the class) will have the same spec. You can use a class as the
|
|
spec for an instance object by passing `instance=True`. The returned mock
|
|
will only be callable if instances of the mock are callable.
|
|
|
|
`create_autospec` also takes arbitrary keyword arguments that are passed to
|
|
the constructor of the created mock.
|
|
|
|
See :ref:`auto-speccing` for examples of how to use auto-speccing with
|
|
`create_autospec` and the `autospec` argument to :func:`patch`.
|
|
|
|
|
|
ANY
|
|
===
|
|
|
|
.. data:: ANY
|
|
|
|
Sometimes you may need to make assertions about *some* of the arguments in a
|
|
call to mock, but either not care about some of the arguments or want to pull
|
|
them individually out of :attr:`~Mock.call_args` and make more complex
|
|
assertions on them.
|
|
|
|
To ignore certain arguments you can pass in objects that compare equal to
|
|
*everything*. Calls to :meth:`~Mock.assert_called_with` and
|
|
:meth:`~Mock.assert_called_once_with` will then succeed no matter what was
|
|
passed in.
|
|
|
|
.. doctest::
|
|
|
|
>>> mock = Mock(return_value=None)
|
|
>>> mock('foo', bar=object())
|
|
>>> mock.assert_called_once_with('foo', bar=ANY)
|
|
|
|
`ANY` can also be used in comparisons with call lists like
|
|
:attr:`~Mock.mock_calls`:
|
|
|
|
.. doctest::
|
|
|
|
>>> m = MagicMock(return_value=None)
|
|
>>> m(1)
|
|
>>> m(1, 2)
|
|
>>> m(object())
|
|
>>> m.mock_calls == [call(1), call(1, 2), ANY]
|
|
True
|
|
|
|
|
|
|
|
FILTER_DIR
|
|
==========
|
|
|
|
.. data:: FILTER_DIR
|
|
|
|
`FILTER_DIR` is a module level variable that controls the way mock objects
|
|
respond to `dir` (only for Python 2.6 or more recent). The default is `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` (or `autospec` of course) then all the
|
|
attributes from the original are shown, even if they haven't been accessed
|
|
yet:
|
|
|
|
.. doctest::
|
|
|
|
>>> dir(Mock())
|
|
['assert_any_call',
|
|
'assert_called_once_with',
|
|
'assert_called_with',
|
|
'assert_has_calls',
|
|
'attach_mock',
|
|
...
|
|
>>> import urllib2
|
|
>>> dir(Mock(spec=urllib2))
|
|
['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` on a `Mock`. If you dislike this
|
|
behaviour you can switch it off by setting the module level switch
|
|
`FILTER_DIR`:
|
|
|
|
.. doctest::
|
|
|
|
>>> 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
|
|
`mock.FILTER_DIR`.
|
|
|
|
|
|
mock_open
|
|
=========
|
|
|
|
.. function:: 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` method of the file handle to return.
|
|
This is an empty string by default.
|
|
|
|
Using `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 :class:`MagicMock` is common enough and fiddly
|
|
enough that a helper function is useful.
|
|
|
|
.. doctest::
|
|
|
|
>>> from mock import mock_open
|
|
>>> m = mock_open()
|
|
>>> with patch('__main__.open', m, create=True):
|
|
... 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:
|
|
|
|
.. doctest::
|
|
|
|
>>> with patch('__main__.open', mock_open(read_data='bibble'), create=True) as m:
|
|
... with open('foo') as h:
|
|
... result = h.read()
|
|
...
|
|
>>> m.assert_called_once_with('foo')
|
|
>>> assert result == 'bibble'
|
|
|
|
|
|
.. _auto-speccing:
|
|
|
|
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 two flaws
|
|
when used to mock out objects from a system under test. One of these flaws is
|
|
specific to the `Mock` api and the other is a more general problem with using
|
|
mock objects.
|
|
|
|
First the problem specific to `Mock`. `Mock` has two assert methods that are
|
|
extremely handy: :meth:`~Mock.assert_called_with` and
|
|
:meth:`~Mock.assert_called_once_with`.
|
|
|
|
.. doctest::
|
|
|
|
>>> mock = Mock(name='Thing', return_value=None)
|
|
>>> mock(1, 2, 3)
|
|
>>> mock.assert_called_once_with(1, 2, 3)
|
|
>>> mock(1, 2, 3)
|
|
>>> mock.assert_called_once_with(1, 2, 3)
|
|
Traceback (most recent call last):
|
|
...
|
|
AssertionError: Expected to be called once. Called 2 times.
|
|
|
|
Because mocks auto-create attributes on demand, and allow you to call them
|
|
with arbitrary arguments, if you misspell one of these assert methods then
|
|
your assertion is gone:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> mock = Mock(name='Thing', return_value=None)
|
|
>>> mock(1, 2, 3)
|
|
>>> mock.assret_called_once_with(4, 5, 6)
|
|
|
|
Your tests can pass silently and incorrectly because of the typo.
|
|
|
|
The second issue is more 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.
|
|
|
|
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.
|
|
|
|
`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:
|
|
|
|
.. doctest::
|
|
|
|
>>> import urllib2
|
|
>>> mock = Mock(spec=urllib2.Request)
|
|
>>> mock.assret_called_with
|
|
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:
|
|
|
|
.. code-block:: pycon
|
|
|
|
>>> mock.has_data()
|
|
<mock.Mock object at 0x...>
|
|
>>> mock.has_data.assret_called_with()
|
|
|
|
Auto-speccing solves this problem. You can either pass `autospec=True` to
|
|
`patch` / `patch.object` or use the `create_autospec` function to create a
|
|
mock with a spec. If you use the `autospec=True` argument to `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:
|
|
|
|
.. doctest::
|
|
|
|
>>> import urllib2
|
|
>>> patcher = patch('__main__.urllib2', autospec=True)
|
|
>>> mock_urllib2 = patcher.start()
|
|
>>> urllib2 is mock_urllib2
|
|
True
|
|
>>> urllib2.Request
|
|
<MagicMock name='urllib2.Request' spec='Request' id='...'>
|
|
|
|
You can see that `urllib2.Request` has a spec. `urllib2.Request` takes two
|
|
arguments in the constructor (one of which is `self`). Here's what happens if
|
|
we try to call it incorrectly:
|
|
|
|
.. doctest::
|
|
|
|
>>> req = urllib2.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):
|
|
|
|
.. doctest::
|
|
|
|
>>> req = urllib2.Request('foo')
|
|
>>> req
|
|
<NonCallableMagicMock name='urllib2.Request()' spec='Request' id='...'>
|
|
|
|
`Request` objects are not callable, so the return value of instantiating our
|
|
mocked out `urllib2.Request` is a non-callable mock. With the spec in place
|
|
any typos in our asserts will raise the correct error:
|
|
|
|
.. doctest::
|
|
|
|
>>> req.add_header('spam', 'eggs')
|
|
<MagicMock name='urllib2.Request().add_header()' id='...'>
|
|
>>> req.add_header.assret_called_with
|
|
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
|
|
:func:`create_autospec` for creating autospecced mocks directly:
|
|
|
|
.. doctest::
|
|
|
|
>>> import urllib2
|
|
>>> mock_urllib2 = create_autospec(urllib2)
|
|
>>> mock_urllib2.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.
|
|
|
|
.. doctest::
|
|
|
|
>>> class Something(object):
|
|
... 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:
|
|
|
|
.. doctest::
|
|
|
|
>>> 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:
|
|
|
|
.. doctest::
|
|
|
|
>>> 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.
|
|
|
|
.. code-block:: python
|
|
|
|
class Something(object):
|
|
a = 33
|
|
|
|
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`):
|
|
|
|
.. doctest::
|
|
|
|
>>> class Something(object):
|
|
... 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` argument:
|
|
|
|
.. doctest::
|
|
|
|
>>> class Something(object):
|
|
... 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='...'>
|
|
|
|
.. note::
|
|
|
|
An additional limitation (currently) with `autospec` is that unbound
|
|
methods on mocked classes *don't* take an "explicit self" as the first
|
|
argument - so this usage will fail with `autospec`.
|
|
|
|
.. doctest::
|
|
|
|
>>> class Foo(object):
|
|
... def foo(self):
|
|
... pass
|
|
...
|
|
>>> Foo.foo(Foo())
|
|
>>> MockFoo = create_autospec(Foo)
|
|
>>> MockFoo.foo(MockFoo())
|
|
Traceback (most recent call last):
|
|
...
|
|
TypeError: <lambda>() takes exactly 1 argument (2 given)
|
|
|
|
The reason is that its very hard to tell the difference between functions,
|
|
unbound methods and staticmethods across Python 2 & 3 and the alternative
|
|
implementations. This restriction may be fixed in future versions.
|
|
|
|
|
|
------
|
|
|
|
.. [#] This only applies to classes or already instantiated objects. Calling
|
|
a mocked class to create a mock instance *does not* create a real instance.
|
|
It is only attribute lookups - along with calls to `dir` - that are done. A
|
|
way round this problem would have been to use `getattr_static
|
|
<http://docs.python.org/dev/library/inspect.html#inspect.getattr_static>`_,
|
|
which can fetch attributes without triggering code execution. Descriptors
|
|
like `classmethod` and `staticmethod` *need* to be fetched correctly though,
|
|
so that their signatures can be mocked correctly.
|