mirror of
https://github.com/thegeeklab/ansible-later.git
synced 2024-11-26 23:00:36 +00:00
381 lines
12 KiB
Plaintext
381 lines
12 KiB
Plaintext
|
Metadata-Version: 2.1
|
||
|
Name: pytest-mock
|
||
|
Version: 1.10.3
|
||
|
Summary: Thin-wrapper around the mock package for easier use with py.test
|
||
|
Home-page: https://github.com/pytest-dev/pytest-mock/
|
||
|
Author: Bruno Oliveira
|
||
|
Author-email: nicoddemus@gmail.com
|
||
|
License: MIT
|
||
|
Keywords: pytest mock
|
||
|
Platform: any
|
||
|
Classifier: Development Status :: 5 - Production/Stable
|
||
|
Classifier: Framework :: Pytest
|
||
|
Classifier: Intended Audience :: Developers
|
||
|
Classifier: License :: OSI Approved :: MIT License
|
||
|
Classifier: Operating System :: OS Independent
|
||
|
Classifier: Programming Language :: Python :: 2
|
||
|
Classifier: Programming Language :: Python :: 2.7
|
||
|
Classifier: Programming Language :: Python :: 3
|
||
|
Classifier: Programming Language :: Python :: 3.4
|
||
|
Classifier: Programming Language :: Python :: 3.5
|
||
|
Classifier: Programming Language :: Python :: 3.6
|
||
|
Classifier: Programming Language :: Python :: 3.7
|
||
|
Classifier: Programming Language :: Python :: 3.8
|
||
|
Classifier: Topic :: Software Development :: Testing
|
||
|
Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*
|
||
|
Requires-Dist: pytest (>=2.7)
|
||
|
Requires-Dist: mock ; python_version < "3.0"
|
||
|
Provides-Extra: dev
|
||
|
Requires-Dist: pre-commit ; extra == 'dev'
|
||
|
Requires-Dist: tox ; extra == 'dev'
|
||
|
|
||
|
===========
|
||
|
pytest-mock
|
||
|
===========
|
||
|
|
||
|
This plugin installs a ``mocker`` fixture which is a thin-wrapper around the patching API
|
||
|
provided by the `mock package <http://pypi.python.org/pypi/mock>`_,
|
||
|
but with the benefit of not having to worry about undoing patches at the end
|
||
|
of a test:
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
import os
|
||
|
|
||
|
class UnixFS:
|
||
|
|
||
|
@staticmethod
|
||
|
def rm(filename):
|
||
|
os.remove(filename)
|
||
|
|
||
|
def test_unix_fs(mocker):
|
||
|
mocker.patch('os.remove')
|
||
|
UnixFS.rm('file')
|
||
|
os.remove.assert_called_once_with('file')
|
||
|
|
||
|
|
||
|
|
||
|
|python| |version| |anaconda| |ci| |appveyor| |coverage| |black|
|
||
|
|
||
|
.. |version| image:: http://img.shields.io/pypi/v/pytest-mock.svg
|
||
|
:target: https://pypi.python.org/pypi/pytest-mock
|
||
|
|
||
|
.. |anaconda| image:: https://img.shields.io/conda/vn/conda-forge/pytest-mock.svg
|
||
|
:target: https://anaconda.org/conda-forge/pytest-mock
|
||
|
|
||
|
.. |ci| image:: http://img.shields.io/travis/pytest-dev/pytest-mock.svg
|
||
|
:target: https://travis-ci.org/pytest-dev/pytest-mock
|
||
|
|
||
|
.. |appveyor| image:: https://ci.appveyor.com/api/projects/status/pid1t7iuwhkm9eh6/branch/master?svg=true
|
||
|
:target: https://ci.appveyor.com/project/pytestbot/pytest-mock
|
||
|
|
||
|
.. |coverage| image:: http://img.shields.io/coveralls/pytest-dev/pytest-mock.svg
|
||
|
:target: https://coveralls.io/r/pytest-dev/pytest-mock
|
||
|
|
||
|
.. |python| image:: https://img.shields.io/pypi/pyversions/pytest-mock.svg
|
||
|
:target: https://pypi.python.org/pypi/pytest-mock/
|
||
|
|
||
|
.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
|
||
|
:target: https://github.com/ambv/black
|
||
|
|
||
|
`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>`_
|
||
|
|
||
|
Usage
|
||
|
=====
|
||
|
|
||
|
The ``mocker`` fixture has the same API as
|
||
|
`mock.patch <https://docs.python.org/3/library/unittest.mock.html#patch>`_,
|
||
|
supporting the same arguments:
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
def test_foo(mocker):
|
||
|
# all valid calls
|
||
|
mocker.patch('os.remove')
|
||
|
mocker.patch.object(os, 'listdir', autospec=True)
|
||
|
mocked_isfile = mocker.patch('os.path.isfile')
|
||
|
|
||
|
The supported methods are:
|
||
|
|
||
|
* `mocker.patch <https://docs.python.org/3/library/unittest.mock.html#patch>`_
|
||
|
* `mocker.patch.object <https://docs.python.org/3/library/unittest.mock.html#patch-object>`_
|
||
|
* `mocker.patch.multiple <https://docs.python.org/3/library/unittest.mock.html#patch-multiple>`_
|
||
|
* `mocker.patch.dict <https://docs.python.org/3/library/unittest.mock.html#patch-dict>`_
|
||
|
* `mocker.stopall <https://docs.python.org/3/library/unittest.mock.html#unittest.mock.patch.stopall>`_
|
||
|
* ``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.
|
||
|
|
||
|
These objects from the ``mock`` module are accessible directly from ``mocker`` for convenience:
|
||
|
|
||
|
* `Mock <https://docs.python.org/3/library/unittest.mock.html#unittest.mock.Mock>`_
|
||
|
* `MagicMock <https://docs.python.org/3/library/unittest.mock.html#unittest.mock.MagicMock>`_
|
||
|
* `PropertyMock <https://docs.python.org/3/library/unittest.mock.html#unittest.mock.PropertyMock>`_
|
||
|
* `ANY <https://docs.python.org/3/library/unittest.mock.html#any>`_
|
||
|
* `DEFAULT <https://docs.python.org/3/library/unittest.mock.html#default>`_ *(Version 1.4)*
|
||
|
* `call <https://docs.python.org/3/library/unittest.mock.html#call>`_ *(Version 1.1)*
|
||
|
* `sentinel <https://docs.python.org/3/library/unittest.mock.html#sentinel>`_ *(Version 1.2)*
|
||
|
* `mock_open <https://docs.python.org/3/library/unittest.mock.html#mock-open>`_
|
||
|
|
||
|
|
||
|
Spy
|
||
|
---
|
||
|
|
||
|
The spy acts exactly like the original method in all cases, except it allows use of `mock`
|
||
|
features with it, like retrieving call count. It also works for class and static methods.
|
||
|
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
def test_spy(mocker):
|
||
|
class Foo(object):
|
||
|
def bar(self):
|
||
|
return 42
|
||
|
|
||
|
foo = Foo()
|
||
|
mocker.spy(foo, 'bar')
|
||
|
assert foo.bar() == 42
|
||
|
assert foo.bar.call_count == 1
|
||
|
|
||
|
Stub
|
||
|
----
|
||
|
|
||
|
|
||
|
The stub is a mock object that accepts any arguments and is useful to test callbacks, for instance.
|
||
|
May be passed a name to be used by the constructed stub object in its repr (useful for debugging).
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
def test_stub(mocker):
|
||
|
def foo(on_something):
|
||
|
on_something('foo', 'bar')
|
||
|
|
||
|
stub = mocker.stub(name='on_something_stub')
|
||
|
|
||
|
foo(stub)
|
||
|
stub.assert_called_once_with('foo', 'bar')
|
||
|
|
||
|
|
||
|
Improved reporting of mock call assertion errors
|
||
|
------------------------------------------------
|
||
|
|
||
|
|
||
|
This plugin monkeypatches the mock library to improve pytest output for failures
|
||
|
of mock call assertions like ``Mock.assert_called_with()`` by hiding internal traceback
|
||
|
entries from the ``mock`` module.
|
||
|
|
||
|
It also adds introspection information on differing call arguments when
|
||
|
calling the helper methods. This features catches `AssertionError` raised in
|
||
|
the method, and uses py.test's own `advanced assertions`_ to return a better
|
||
|
diff::
|
||
|
|
||
|
|
||
|
mocker = <pytest_mock.MockFixture object at 0x0381E2D0>
|
||
|
|
||
|
def test(mocker):
|
||
|
m = mocker.Mock()
|
||
|
m('fo')
|
||
|
> m.assert_called_once_with('', bar=4)
|
||
|
E AssertionError: Expected call: mock('', bar=4)
|
||
|
E Actual call: mock('fo')
|
||
|
E
|
||
|
E pytest introspection follows:
|
||
|
E
|
||
|
E Args:
|
||
|
E assert ('fo',) == ('',)
|
||
|
E At index 0 diff: 'fo' != ''
|
||
|
E Use -v to get the full diff
|
||
|
E Kwargs:
|
||
|
E assert {} == {'bar': 4}
|
||
|
E Right contains more items:
|
||
|
E {'bar': 4}
|
||
|
E Use -v to get the full diff
|
||
|
|
||
|
|
||
|
test_foo.py:6: AssertionError
|
||
|
========================== 1 failed in 0.03 seconds ===========================
|
||
|
|
||
|
|
||
|
This is useful when asserting mock calls with many/nested arguments and trying
|
||
|
to quickly see the difference.
|
||
|
|
||
|
This feature is probably safe, but if you encounter any problems it can be disabled in
|
||
|
your ``pytest.ini`` file:
|
||
|
|
||
|
.. code-block:: ini
|
||
|
|
||
|
[pytest]
|
||
|
mock_traceback_monkeypatch = false
|
||
|
|
||
|
Note that this feature is automatically disabled with the ``--tb=native`` option. The underlying
|
||
|
mechanism used to suppress traceback entries from ``mock`` module does not work with that option
|
||
|
anyway plus it generates confusing messages on Python 3.5 due to exception chaining
|
||
|
|
||
|
.. _advanced assertions: http://pytest.org/latest/assert.html
|
||
|
|
||
|
|
||
|
Use standalone "mock" package
|
||
|
-----------------------------
|
||
|
|
||
|
*New in version 1.4.0.*
|
||
|
|
||
|
Python 3 users might want to use a newest version of the ``mock`` package as published on PyPI
|
||
|
than the one that comes with the Python distribution.
|
||
|
|
||
|
.. code-block:: ini
|
||
|
|
||
|
[pytest]
|
||
|
mock_use_standalone_module = true
|
||
|
|
||
|
This will force the plugin to import ``mock`` instead of the ``unittest.mock`` module bundled with
|
||
|
Python 3.4+. Note that this option is only used in Python 3+, as Python 2 users only have the option
|
||
|
to use the ``mock`` package from PyPI anyway.
|
||
|
|
||
|
|
||
|
Requirements
|
||
|
============
|
||
|
|
||
|
* Python 2.7, Python 3.4+
|
||
|
* pytest
|
||
|
* mock (for Python 2)
|
||
|
|
||
|
|
||
|
Install
|
||
|
=======
|
||
|
|
||
|
Install using `pip <http://pip-installer.org/>`_:
|
||
|
|
||
|
.. code-block:: console
|
||
|
|
||
|
$ pip install pytest-mock
|
||
|
|
||
|
Changelog
|
||
|
=========
|
||
|
|
||
|
Please consult the `changelog page`_.
|
||
|
|
||
|
.. _changelog page: https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst
|
||
|
|
||
|
Why bother with a plugin?
|
||
|
=========================
|
||
|
|
||
|
There are a number of different ``patch`` usages in the standard ``mock`` API,
|
||
|
but IMHO they don't scale very well when you have more than one or two
|
||
|
patches to apply.
|
||
|
|
||
|
It may lead to an excessive nesting of ``with`` statements, breaking the flow
|
||
|
of the test:
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
import mock
|
||
|
|
||
|
def test_unix_fs():
|
||
|
with mock.patch('os.remove'):
|
||
|
UnixFS.rm('file')
|
||
|
os.remove.assert_called_once_with('file')
|
||
|
|
||
|
with mock.patch('os.listdir'):
|
||
|
assert UnixFS.ls('dir') == expected
|
||
|
# ...
|
||
|
|
||
|
with mock.patch('shutil.copy'):
|
||
|
UnixFS.cp('src', 'dst')
|
||
|
# ...
|
||
|
|
||
|
|
||
|
One can use ``patch`` as a decorator to improve the flow of the test:
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
@mock.patch('os.remove')
|
||
|
@mock.patch('os.listdir')
|
||
|
@mock.patch('shutil.copy')
|
||
|
def test_unix_fs(mocked_copy, mocked_listdir, mocked_remove):
|
||
|
UnixFS.rm('file')
|
||
|
os.remove.assert_called_once_with('file')
|
||
|
|
||
|
assert UnixFS.ls('dir') == expected
|
||
|
# ...
|
||
|
|
||
|
UnixFS.cp('src', 'dst')
|
||
|
# ...
|
||
|
|
||
|
But this poses a few disadvantages:
|
||
|
|
||
|
- test functions must receive the mock objects as parameter, even if you don't plan to
|
||
|
access them directly; also, order depends on the order of the decorated ``patch``
|
||
|
functions;
|
||
|
- receiving the mocks as parameters doesn't mix nicely with pytest's approach of
|
||
|
naming fixtures as parameters, or ``pytest.mark.parametrize``;
|
||
|
- you can't easily undo the mocking during the test execution;
|
||
|
|
||
|
|
||
|
**Note about usage as context manager**
|
||
|
|
||
|
Although mocker's API is intentionally the same as ``mock.patch``'s, its use
|
||
|
as context manager and function decorator is **not** supported through the
|
||
|
fixture. The purpose of this plugin is to make the use of context managers and
|
||
|
function decorators for mocking unnecessary. Indeed, trying to use the
|
||
|
functionality in ``mocker`` in this manner can lead to non-intuitive errors:
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
def test_context_manager(mocker):
|
||
|
a = A()
|
||
|
with mocker.patch.object(a, 'doIt', return_value=True, autospec=True):
|
||
|
assert a.doIt() == True
|
||
|
|
||
|
.. code-block:: console
|
||
|
|
||
|
================================== FAILURES ===================================
|
||
|
____________________________ test_context_manager _____________________________
|
||
|
in test_context_manager
|
||
|
with mocker.patch.object(a, 'doIt', return_value=True, autospec=True):
|
||
|
E AttributeError: __exit__
|
||
|
|
||
|
You can however use ``mocker.mock_module`` to access the underlying ``mock``
|
||
|
module, e.g. to return a context manager in a fixture that mocks something
|
||
|
temporarily:
|
||
|
|
||
|
.. code-block:: python
|
||
|
|
||
|
@pytest.fixture
|
||
|
def fixture_cm(mocker):
|
||
|
@contextlib.contextmanager
|
||
|
def my_cm():
|
||
|
def mocked():
|
||
|
pass
|
||
|
|
||
|
with mocker.mock_module.patch.object(SomeClass, 'method', mocked):
|
||
|
yield
|
||
|
return my_cm
|
||
|
|
||
|
|
||
|
Contributing
|
||
|
============
|
||
|
|
||
|
Contributions are welcome! After cloning the repository, create a virtual env
|
||
|
and install ``pytest-mock`` in editable mode with ``dev`` extras:
|
||
|
|
||
|
.. code-block:: console
|
||
|
|
||
|
$ pip install --editable .[dev]
|
||
|
$ pre-commit install
|
||
|
|
||
|
Tests are run with ``tox``, you can run the baseline environments before submitting a PR:
|
||
|
|
||
|
.. code-block:: console
|
||
|
|
||
|
$ tox -e py27,py36,linting
|
||
|
|
||
|
Style checks and formatting are done automatically during commit courtesy of
|
||
|
`pre-commit <https://pre-commit.com>`_.
|
||
|
|
||
|
License
|
||
|
=======
|
||
|
|
||
|
Distributed under the terms of the `MIT`_ license.
|
||
|
|
||
|
.. _MIT: https://github.com/pytest-dev/pytest-mock/blob/master/LICENSE
|
||
|
|
||
|
|