1===========
2pytest-mock
3===========
4
5This plugin installs a ``mocker`` fixture which is a thin-wrapper around the patching API
6provided by the `mock package <http://pypi.python.org/pypi/mock>`_,
7but with the benefit of not having to worry about undoing patches at the end
8of a test:
9
10.. code-block:: python
11
12    import os
13
14    class UnixFS:
15
16        @staticmethod
17        def rm(filename):
18            os.remove(filename)
19
20    def test_unix_fs(mocker):
21        mocker.patch('os.remove')
22        UnixFS.rm('file')
23        os.remove.assert_called_once_with('file')
24
25
26
27|python| |version| |anaconda| |ci| |appveyor| |coverage| |black|
28
29.. |version| image:: http://img.shields.io/pypi/v/pytest-mock.svg
30  :target: https://pypi.python.org/pypi/pytest-mock
31
32.. |anaconda| image:: https://img.shields.io/conda/vn/conda-forge/pytest-mock.svg
33    :target: https://anaconda.org/conda-forge/pytest-mock
34
35.. |ci| image:: http://img.shields.io/travis/pytest-dev/pytest-mock.svg
36  :target: https://travis-ci.org/pytest-dev/pytest-mock
37
38.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/pid1t7iuwhkm9eh6/branch/master?svg=true
39  :target: https://ci.appveyor.com/project/pytestbot/pytest-mock
40
41.. |coverage| image:: http://img.shields.io/coveralls/pytest-dev/pytest-mock.svg
42  :target: https://coveralls.io/r/pytest-dev/pytest-mock
43
44.. |python| image:: https://img.shields.io/pypi/pyversions/pytest-mock.svg
45  :target: https://pypi.python.org/pypi/pytest-mock/
46
47.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
48  :target: https://github.com/ambv/black
49
50`Professionally supported pytest-mock is now available <https://tidelift.com/subscription/pkg/pypi-pytest_mock?utm_source=pypi-pytest-mock&utm_medium=referral&utm_campaign=readme>`_
51
52Usage
53=====
54
55The ``mocker`` fixture has the same API as
56`mock.patch <https://docs.python.org/3/library/unittest.mock.html#patch>`_,
57supporting the same arguments:
58
59.. code-block:: python
60
61    def test_foo(mocker):
62        # all valid calls
63        mocker.patch('os.remove')
64        mocker.patch.object(os, 'listdir', autospec=True)
65        mocked_isfile = mocker.patch('os.path.isfile')
66
67The supported methods are:
68
69* `mocker.patch <https://docs.python.org/3/library/unittest.mock.html#patch>`_
70* `mocker.patch.object <https://docs.python.org/3/library/unittest.mock.html#patch-object>`_
71* `mocker.patch.multiple <https://docs.python.org/3/library/unittest.mock.html#patch-multiple>`_
72* `mocker.patch.dict <https://docs.python.org/3/library/unittest.mock.html#patch-dict>`_
73* `mocker.stopall <https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch.stopall>`_
74* ``mocker.resetall()``: calls `reset_mock() <https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock.reset_mock>`_ in all mocked objects up to this point.
75
76These objects from the ``mock`` module are accessible directly from ``mocker`` for convenience:
77
78* `Mock <https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock>`_
79* `MagicMock <https://docs.python.org/3/library/unittest.mock.html#unittest.mock.MagicMock>`_
80* `PropertyMock <https://docs.python.org/3/library/unittest.mock.html#unittest.mock.PropertyMock>`_
81* `ANY <https://docs.python.org/3/library/unittest.mock.html#any>`_
82* `DEFAULT <https://docs.python.org/3/library/unittest.mock.html#default>`_ *(Version 1.4)*
83* `call <https://docs.python.org/3/library/unittest.mock.html#call>`_ *(Version 1.1)*
84* `sentinel <https://docs.python.org/3/library/unittest.mock.html#sentinel>`_ *(Version 1.2)*
85* `mock_open <https://docs.python.org/3/library/unittest.mock.html#mock-open>`_
86
87
88Spy
89---
90
91The spy acts exactly like the original method in all cases, except it allows use of `mock`
92features with it, like retrieving call count. It also works for class and static methods.
93
94
95.. code-block:: python
96
97    def test_spy(mocker):
98        class Foo(object):
99            def bar(self):
100                return 42
101
102        foo = Foo()
103        mocker.spy(foo, 'bar')
104        assert foo.bar() == 42
105        assert foo.bar.call_count == 1
106
107Stub
108----
109
110
111The stub is a mock object that accepts any arguments and is useful to test callbacks, for instance.
112May be passed a name to be used by the constructed stub object in its repr (useful for debugging).
113
114.. code-block:: python
115
116    def test_stub(mocker):
117        def foo(on_something):
118            on_something('foo', 'bar')
119
120        stub = mocker.stub(name='on_something_stub')
121
122        foo(stub)
123        stub.assert_called_once_with('foo', 'bar')
124
125
126Improved reporting of mock call assertion errors
127------------------------------------------------
128
129
130This plugin monkeypatches the mock library to improve pytest output for failures
131of mock call assertions like ``Mock.assert_called_with()`` by hiding internal traceback
132entries from the ``mock`` module.
133
134It also adds introspection information on differing call arguments when
135calling the helper methods. This features catches `AssertionError` raised in
136the method, and uses py.test's own `advanced assertions`_ to return a better
137diff::
138
139
140    mocker = <pytest_mock.MockFixture object at 0x0381E2D0>
141
142        def test(mocker):
143            m = mocker.Mock()
144            m('fo')
145    >       m.assert_called_once_with('', bar=4)
146    E       AssertionError: Expected call: mock('', bar=4)
147    E       Actual call: mock('fo')
148    E
149    E       pytest introspection follows:
150    E
151    E       Args:
152    E       assert ('fo',) == ('',)
153    E         At index 0 diff: 'fo' != ''
154    E         Use -v to get the full diff
155    E       Kwargs:
156    E       assert {} == {'bar': 4}
157    E         Right contains more items:
158    E         {'bar': 4}
159    E         Use -v to get the full diff
160
161
162    test_foo.py:6: AssertionError
163    ========================== 1 failed in 0.03 seconds ===========================
164
165
166This is useful when asserting mock calls with many/nested arguments and trying
167to quickly see the difference.
168
169This feature is probably safe, but if you encounter any problems it can be disabled in
170your ``pytest.ini`` file:
171
172.. code-block:: ini
173
174    [pytest]
175    mock_traceback_monkeypatch = false
176
177Note that this feature is automatically disabled with the ``--tb=native`` option. The underlying
178mechanism used to suppress traceback entries from ``mock`` module does not work with that option
179anyway plus it generates confusing messages on Python 3.5 due to exception chaining
180
181.. _advanced assertions: http://pytest.org/latest/assert.html
182
183
184Use standalone "mock" package
185-----------------------------
186
187*New in version 1.4.0.*
188
189Python 3 users might want to use a newest version of the ``mock`` package as published on PyPI
190than the one that comes with the Python distribution.
191
192.. code-block:: ini
193
194    [pytest]
195    mock_use_standalone_module = true
196
197This will force the plugin to import ``mock`` instead of the ``unittest.mock`` module bundled with
198Python 3.4+. Note that this option is only used in Python 3+, as Python 2 users only have the option
199to use the ``mock`` package from PyPI anyway.
200
201
202Requirements
203============
204
205* Python 2.7, Python 3.4+
206* pytest
207* mock (for Python 2)
208
209
210Install
211=======
212
213Install using `pip <http://pip-installer.org/>`_:
214
215.. code-block:: console
216
217    $ pip install pytest-mock
218
219Changelog
220=========
221
222Please consult the `changelog page`_.
223
224.. _changelog page: https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst
225
226Why bother with a plugin?
227=========================
228
229There are a number of different ``patch`` usages in the standard ``mock`` API,
230but IMHO they don't scale very well when you have more than one or two
231patches to apply.
232
233It may lead to an excessive nesting of ``with`` statements, breaking the flow
234of the test:
235
236.. code-block:: python
237
238    import mock
239
240    def test_unix_fs():
241        with mock.patch('os.remove'):
242            UnixFS.rm('file')
243            os.remove.assert_called_once_with('file')
244
245            with mock.patch('os.listdir'):
246                assert UnixFS.ls('dir') == expected
247                # ...
248
249        with mock.patch('shutil.copy'):
250            UnixFS.cp('src', 'dst')
251            # ...
252
253
254One can use ``patch`` as a decorator to improve the flow of the test:
255
256.. code-block:: python
257
258    @mock.patch('os.remove')
259    @mock.patch('os.listdir')
260    @mock.patch('shutil.copy')
261    def test_unix_fs(mocked_copy, mocked_listdir, mocked_remove):
262        UnixFS.rm('file')
263        os.remove.assert_called_once_with('file')
264
265        assert UnixFS.ls('dir') == expected
266        # ...
267
268        UnixFS.cp('src', 'dst')
269        # ...
270
271But this poses a few disadvantages:
272
273- test functions must receive the mock objects as parameter, even if you don't plan to
274  access them directly; also, order depends on the order of the decorated ``patch``
275  functions;
276- receiving the mocks as parameters doesn't mix nicely with pytest's approach of
277  naming fixtures as parameters, or ``pytest.mark.parametrize``;
278- you can't easily undo the mocking during the test execution;
279
280
281**Note about usage as context manager**
282
283Although mocker's API is intentionally the same as ``mock.patch``'s, its use
284as context manager and function decorator is **not** supported through the
285fixture. The purpose of this plugin is to make the use of context managers and
286function decorators for mocking unnecessary. Indeed, trying to use the
287functionality in ``mocker`` in this manner can lead to non-intuitive errors:
288
289.. code-block:: python
290
291    def test_context_manager(mocker):
292        a = A()
293        with mocker.patch.object(a, 'doIt', return_value=True, autospec=True):
294            assert a.doIt() == True
295
296.. code-block:: console
297
298    ================================== FAILURES ===================================
299    ____________________________ test_context_manager _____________________________
300    in test_context_manager
301        with mocker.patch.object(a, 'doIt', return_value=True, autospec=True):
302    E   AttributeError: __exit__
303
304You can however use ``mocker.mock_module`` to access the underlying ``mock``
305module, e.g. to return a context manager in a fixture that mocks something
306temporarily:
307
308.. code-block:: python
309
310    @pytest.fixture
311    def fixture_cm(mocker):
312        @contextlib.contextmanager
313        def my_cm():
314            def mocked():
315                pass
316
317            with mocker.mock_module.patch.object(SomeClass, 'method', mocked):
318                yield
319        return my_cm
320
321
322Contributing
323============
324
325Contributions are welcome! After cloning the repository, create a virtual env
326and install ``pytest-mock`` in editable mode with ``dev`` extras:
327
328.. code-block:: console
329
330    $ pip install --editable .[dev]
331    $ pre-commit install
332
333Tests are run with ``tox``, you can run the baseline environments before submitting a PR:
334
335.. code-block:: console
336
337    $ tox -e py27,py36,linting
338
339Style checks and formatting are done automatically during commit courtesy of
340`pre-commit <https://pre-commit.com>`_.
341
342License
343=======
344
345Distributed under the terms of the `MIT`_ license.
346
347.. _MIT: https://github.com/pytest-dev/pytest-mock/blob/master/LICENSE
348