Import pytest-mock_1.3.0.orig.tar.gz

This commit is contained in:
Vincent Bernat 2016-11-01 20:01:57 +01:00
parent bdae00c258
commit cc3055ebc3
17 changed files with 2003 additions and 1655 deletions

58
.gitignore vendored Normal file
View file

@ -0,0 +1,58 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
env/
bin/
build/
develop-eggs/
dist/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg*
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Rope
.ropeproject
# Django stuff:
*.log
*.pot
# Sphinx documentation
docs/_build/
# Virtual Envs
.env*
_pytest_mock_version.py

25
.travis.yml Normal file
View file

@ -0,0 +1,25 @@
language: python
python:
- "3.5"
- "3.6-dev"
install:
- pip install tox coveralls
script:
- if [[ $TRAVIS_PYTHON_VERSION != 3.6-dev ]]; then tox; fi
- if [[ $TRAVIS_PYTHON_VERSION == 3.6-dev ]]; then tox -e py36-pytest28,py36-pytest29,py36-pytest30; fi
after_success:
- coveralls
deploy:
provider: pypi
user: nicoddemus
password:
secure: bB4adUZVIkt31cmNklskyIDNehujKToGnStnlunp7P8CBF6CGeNqkYU17emAPvfZbTb/ClUpiO9r6AD1ej32Uyr+I8qUyhuYtHG3JGp+WRR/tw+ytAZIJ9i+PMjBv1RAdyLENJ/Tx0LKHKsABr8dQIieLFqKZJuT77f/5ZkvI/U=
on:
tags: true
distributions: sdist bdist_wheel
repo: pytest-dev/pytest-mock
condition: $TRAVIS_PYTHON_VERSION = 3.5

214
CHANGELOG.rst Normal file
View file

@ -0,0 +1,214 @@
1.3
---
* Add support for Python 3.6. Thanks `@hackebrot`_ for the report (`#59`_).
* ``mock.mock_open`` is now aliased as ``mocker.mock_open`` for convenience.
Thanks `@pokidovea`_ for the PR (`#66`_).
.. _@hackebrot: https://github.com/hackebrot
.. _@pokidovea: https://github.com/pokidovea
.. _#59: https://github.com/pytest-dev/pytest-mock/issues/59
.. _#66: https://github.com/pytest-dev/pytest-mock/pull/66
1.2
---
* Try to import ``mock`` first instead of ``unittest.mock``. This gives the user flexibility
to install a newer ``mock`` version from PyPI instead of using the one available in the
Python distribution.
Thanks `@wcooley`_ for the PR (`#54`_).
* ``mock.sentinel`` is now aliased as ``mocker.sentinel`` for convenience.
Thanks `@kjwilcox`_ for the PR (`#56`_).
.. _@wcooley: https://github.com/wcooley
.. _@kjwilcox: https://github.com/kjwilcox
.. _#54: https://github.com/pytest-dev/pytest-mock/issues/54
.. _#56: https://github.com/pytest-dev/pytest-mock/pull/56
1.1
---
* From this version onward, ``pytest-mock`` is licensed under the `MIT`_ license (`#45`_).
* Now the plugin also adds introspection information on differing call arguments when
calling helper methods such as ``assert_called_once_with``. The extra introspection
information is similar to pytest's and can be disabled with the ``mock_traceback_monkeypatch``
option.
Thanks `@asfaltboy`_ for the PR (`#36`_).
* ``mocker.stub()`` now allows passing in the name for the constructed `Mock
<https://docs.python.org/3/library/unittest.mock.html#the-mock-class>`_
object instead of having to set it using the internal ``_mock_name`` attribute
directly. This is useful for debugging as the name is used in the mock's
``repr`` string as well as related assertion failure messages.
Thanks `@jurko-gospodnetic`_ for the PR (`#40`_).
* Monkey patching ``mock`` module for friendlier tracebacks 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 (`#44`_).
Thanks `@blueyed`_ for the report.
* ``mock.call`` is now aliased as ``mocker.call`` for convenience.
Thanks `@jhermann`_ for the PR (`#49`_).
.. _@jurko-gospodnetic: https://github.com/jurko-gospodnetic
.. _@asfaltboy: https://github.com/asfaltboy
.. _@jhermann: https://github.com/jhermann
.. _#45: https://github.com/pytest-dev/pytest-mock/issues/45
.. _#36: https://github.com/pytest-dev/pytest-mock/issues/36
.. _#40: https://github.com/pytest-dev/pytest-mock/issues/40
.. _#44: https://github.com/pytest-dev/pytest-mock/issues/44
.. _#49: https://github.com/pytest-dev/pytest-mock/issues/49
.. _MIT: https://github.com/pytest-dev/pytest-mock/blob/master/LICENSE
1.0
---
* Fix AttributeError with ``mocker.spy`` when spying on inherited methods
(`#42`_). Thanks `@blueyed`_ for the PR.
.. _@blueyed: https://github.com/blueyed
.. _#42: https://github.com/pytest-dev/pytest-mock/issues/42
0.11.0
------
* `PropertyMock <https://docs.python.org/3/library/unittest.mock.html#unittest.mock.PropertyMock>`_
is now accessible from ``mocker``.
Thanks `@satyrius`_ for the PR (`#32`_).
* Fix regression using one of the ``assert_*`` methods in patched
functions which receive a parameter named ``method``.
Thanks `@sagarchalise`_ for the report (`#31`_).
.. _@sagarchalise: https://github.com/sagarchalise
.. _@satyrius: https://github.com/satyrius
.. _#31: https://github.com/pytest-dev/pytest-mock/issues/31
.. _#32: https://github.com/pytest-dev/pytest-mock/issues/32
0.10.1
------
* Fix regression in frozen tests due to ``distutils`` import dependency.
Thanks `@The-Compiler`_ for the report (`#29`_).
* Fix regression when using ``pytest-mock`` with ``pytest-2.7.X``.
Thanks `@akscram`_ for the report (`#28`_).
.. _@akscram: https://github.com/Chronial
.. _#28: https://github.com/pytest-dev/pytest-mock/issues/28
.. _#29: https://github.com/pytest-dev/pytest-mock/issues/29
0.10
----
* ``pytest-mock`` now monkeypatches the ``mock`` library to improve pytest output
for failures of mock call assertions like ``Mock.assert_called_with()``.
Thanks to `@Chronial`_ for idea and PR (`#26`_, `#27`_)!
.. _@Chronial: https://github.com/Chronial
.. _#26: https://github.com/pytest-dev/pytest-mock/issues/26
.. _#27: https://github.com/pytest-dev/pytest-mock/issues/27
0.9.0
-----
* New ``mocker.resetall`` function, which calls ``reset_mock()`` in all mocked
objects up to that point. Thanks to `@mathrick`_ for the PR!
0.8.1
-----
* ``pytest-mock`` is now also available as a wheel. Thanks `@rouge8`_ for the PR!
0.8.0
-----
* ``mock.ANY`` is now accessible from the mocker fixture (`#17`_), thanks `@tigarmo`_ for the PR!
.. _#17: https://github.com/pytest-dev/pytest-qt/issues/17
0.7.0
-----
Thanks to `@fogo`_, mocker.spy can now prey upon staticmethods and classmethods. :smile:
0.6.0
-----
* Two new auxiliary methods, ``spy`` and ``stub``. See ``README`` for usage.
(Thanks `@fogo`_ for complete PR!)
0.5.0
-----
* ``Mock`` and ``MagicMock`` are now accessible from the ``mocker`` fixture,
many thanks to `@marcwebbie`_ for the complete PR!
0.4.3
-----
* ``mocker`` fixture now returns the same object (`#8`_). Many thanks to `@RonnyPfannschmidt`_ for the PR!
.. _#8: https://github.com/pytest-dev/pytest-qt/issues/8
0.4.2
-----
* Small fix, no longer using wheel as an alternate package since it
conditionally depends on mock module based on Python version,
as Python >= 3.3 already includes ``unittest.mock``.
Many thanks to `@The-Compiler`_ for letting me know and providing a PR with the fix!
0.4.1
-----
* Small release that just uses ``pytest_mock`` as the name of the plugin,
instead of ``pytest-mock``: this makes it simple to depend on this plugin
explicitly using ``pytest_plugins`` module variable mechanism.
0.4.0
-----
* Changed fixture name from ``mock`` into ``mocker`` because it conflicted
with the actual mock module, which made using it awkward when access to both
the module and the fixture were required within a test.
Thanks `@kmosher`_ for request and discussion in `#4`_. :smile:
.. _#4: https://github.com/pytest-dev/pytest-qt/issues/4
0.3.0
-----
* Fixed bug `#2`_, where a patch would not be uninstalled correctly after
patching the same object twice.
0.2.0
-----
* Added ``patch.dict`` support.
0.1.0
-----
First release.
.. _#2: https://github.com/pytest-dev/pytest-qt/issues/2
.. _@mathrick: https://github.com/mathrick
.. _@tigarmo: https://github.com/tigarmo
.. _@rouge8: https://github.com/rouge8
.. _@fogo: https://github.com/fogo
.. _@marcwebbie: https://github.com/marcwebbie
.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
.. _@The-Compiler: https://github.com/The-Compiler
.. _@kmosher: https://github.com/kmosher

40
LICENSE
View file

@ -1,21 +1,21 @@
MIT License
Copyright (c) [2016] [Bruno Oliveira]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
MIT License
Copyright (c) [2016] [Bruno Oliveira]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,2 +1,2 @@
include README.md
include README.md
include LICENSE

566
PKG-INFO
View file

@ -1,282 +1,284 @@
Metadata-Version: 1.1
Name: pytest-mock
Version: 1.2
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
Description: ===========
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
def test_unix_fs(mocker):
mocker.patch('os.remove')
UnixFS.rm('file')
os.remove.assert_called_once_with('file')
.. Using PNG badges because PyPI doesn't support SVG
|python| |version| |downloads| |ci| |appveyor| |coverage|
.. |version| image:: http://img.shields.io/pypi/v/pytest-mock.png
:target: https://pypi.python.org/pypi/pytest-mock
.. |downloads| image:: http://img.shields.io/pypi/dm/pytest-mock.png
:target: https://pypi.python.org/pypi/pytest-mock
.. |ci| image:: http://img.shields.io/travis/pytest-dev/pytest-mock.png
: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.png
: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/
Usage
=====
The ``mocker`` fixture has the same API as
`mock.patch <http://www.voidspace.org.uk/python/mock/patch.html#patch-decorators>`_,
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``: see http://www.voidspace.org.uk/python/mock/patch.html#patch.
* ``mocker.patch.object``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-object.
* ``mocker.patch.multiple``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-multiple.
* ``mocker.patch.dict``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-dict.
* ``mocker.stopall()``: stops all active patches up to this point.
* ``mocker.resetall()``: calls ``reset_mock()`` in all mocked objects up to this point.
Some 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>`_
* `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)*
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::
m = mocker.patch.object(DS, 'create_char')
DS().create_char('Raistlin', class_='mag', gift=12)
> m.assert_called_once_with('Raistlin', class_='mage', gift=12)
E assert {'class_': 'mag', 'gift': 12} == {'class_': 'mage', 'gift': 12}
E Omitting 1 identical items, use -v to show
E Differing items:
E {'class_': 'mag'} != {'class_': 'mage'}
E Use -v to get the full diff
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: https://pytest.org/latest/assert.html
Requirements
============
* Python 2.6+, Python 3.3+
* 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**
Although mocker's API is intentionally the same as ``mock.patch``'s, its uses as context managers and function decorators are **not** supported. 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__
License
=======
Distributed under the terms of the `MIT`_ license.
.. _MIT: https://github.com/pytest-dev/pytest-mock/blob/master/LICENSE
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.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Topic :: Software Development :: Testing
Metadata-Version: 1.1
Name: pytest-mock
Version: 1.3.0
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
Description: ===========
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
def test_unix_fs(mocker):
mocker.patch('os.remove')
UnixFS.rm('file')
os.remove.assert_called_once_with('file')
.. Using PNG badges because PyPI doesn't support SVG
|python| |version| |downloads| |ci| |appveyor| |coverage|
.. |version| image:: http://img.shields.io/pypi/v/pytest-mock.png
:target: https://pypi.python.org/pypi/pytest-mock
.. |downloads| image:: http://img.shields.io/pypi/dm/pytest-mock.png
:target: https://pypi.python.org/pypi/pytest-mock
.. |ci| image:: http://img.shields.io/travis/pytest-dev/pytest-mock.png
: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.png
: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/
Usage
=====
The ``mocker`` fixture has the same API as
`mock.patch <http://www.voidspace.org.uk/python/mock/patch.html#patch-decorators>`_,
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``: see http://www.voidspace.org.uk/python/mock/patch.html#patch.
* ``mocker.patch.object``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-object.
* ``mocker.patch.multiple``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-multiple.
* ``mocker.patch.dict``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-dict.
* ``mocker.stopall()``: stops all active patches up to this point.
* ``mocker.resetall()``: calls ``reset_mock()`` in all mocked objects up to this point.
Some 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>`_
* `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::
m = mocker.patch.object(DS, 'create_char')
DS().create_char('Raistlin', class_='mag', gift=12)
> m.assert_called_once_with('Raistlin', class_='mage', gift=12)
E assert {'class_': 'mag', 'gift': 12} == {'class_': 'mage', 'gift': 12}
E Omitting 1 identical items, use -v to show
E Differing items:
E {'class_': 'mag'} != {'class_': 'mage'}
E Use -v to get the full diff
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: https://pytest.org/latest/assert.html
Requirements
============
* Python 2.6+, Python 3.3+
* 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**
Although mocker's API is intentionally the same as ``mock.patch``'s, its uses as context managers and function decorators are **not** supported. 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__
License
=======
Distributed under the terms of the `MIT`_ license.
.. _MIT: https://github.com/pytest-dev/pytest-mock/blob/master/LICENSE
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.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Topic :: Software Development :: Testing

View file

@ -1,259 +1,260 @@
===========
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
def test_unix_fs(mocker):
mocker.patch('os.remove')
UnixFS.rm('file')
os.remove.assert_called_once_with('file')
.. Using PNG badges because PyPI doesn't support SVG
|python| |version| |downloads| |ci| |appveyor| |coverage|
.. |version| image:: http://img.shields.io/pypi/v/pytest-mock.png
:target: https://pypi.python.org/pypi/pytest-mock
.. |downloads| image:: http://img.shields.io/pypi/dm/pytest-mock.png
:target: https://pypi.python.org/pypi/pytest-mock
.. |ci| image:: http://img.shields.io/travis/pytest-dev/pytest-mock.png
: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.png
: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/
Usage
=====
The ``mocker`` fixture has the same API as
`mock.patch <http://www.voidspace.org.uk/python/mock/patch.html#patch-decorators>`_,
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``: see http://www.voidspace.org.uk/python/mock/patch.html#patch.
* ``mocker.patch.object``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-object.
* ``mocker.patch.multiple``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-multiple.
* ``mocker.patch.dict``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-dict.
* ``mocker.stopall()``: stops all active patches up to this point.
* ``mocker.resetall()``: calls ``reset_mock()`` in all mocked objects up to this point.
Some 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>`_
* `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)*
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::
m = mocker.patch.object(DS, 'create_char')
DS().create_char('Raistlin', class_='mag', gift=12)
> m.assert_called_once_with('Raistlin', class_='mage', gift=12)
E assert {'class_': 'mag', 'gift': 12} == {'class_': 'mage', 'gift': 12}
E Omitting 1 identical items, use -v to show
E Differing items:
E {'class_': 'mag'} != {'class_': 'mage'}
E Use -v to get the full diff
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: https://pytest.org/latest/assert.html
Requirements
============
* Python 2.6+, Python 3.3+
* 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**
Although mocker's API is intentionally the same as ``mock.patch``'s, its uses as context managers and function decorators are **not** supported. 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__
License
=======
Distributed under the terms of the `MIT`_ license.
.. _MIT: https://github.com/pytest-dev/pytest-mock/blob/master/LICENSE
===========
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
def test_unix_fs(mocker):
mocker.patch('os.remove')
UnixFS.rm('file')
os.remove.assert_called_once_with('file')
.. Using PNG badges because PyPI doesn't support SVG
|python| |version| |downloads| |ci| |appveyor| |coverage|
.. |version| image:: http://img.shields.io/pypi/v/pytest-mock.png
:target: https://pypi.python.org/pypi/pytest-mock
.. |downloads| image:: http://img.shields.io/pypi/dm/pytest-mock.png
:target: https://pypi.python.org/pypi/pytest-mock
.. |ci| image:: http://img.shields.io/travis/pytest-dev/pytest-mock.png
: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.png
: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/
Usage
=====
The ``mocker`` fixture has the same API as
`mock.patch <http://www.voidspace.org.uk/python/mock/patch.html#patch-decorators>`_,
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``: see http://www.voidspace.org.uk/python/mock/patch.html#patch.
* ``mocker.patch.object``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-object.
* ``mocker.patch.multiple``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-multiple.
* ``mocker.patch.dict``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-dict.
* ``mocker.stopall()``: stops all active patches up to this point.
* ``mocker.resetall()``: calls ``reset_mock()`` in all mocked objects up to this point.
Some 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>`_
* `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::
m = mocker.patch.object(DS, 'create_char')
DS().create_char('Raistlin', class_='mag', gift=12)
> m.assert_called_once_with('Raistlin', class_='mage', gift=12)
E assert {'class_': 'mag', 'gift': 12} == {'class_': 'mage', 'gift': 12}
E Omitting 1 identical items, use -v to show
E Differing items:
E {'class_': 'mag'} != {'class_': 'mage'}
E Use -v to get the full diff
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: https://pytest.org/latest/assert.html
Requirements
============
* Python 2.6+, Python 3.3+
* 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**
Although mocker's API is intentionally the same as ``mock.patch``'s, its uses as context managers and function decorators are **not** supported. 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__
License
=======
Distributed under the terms of the `MIT`_ license.
.. _MIT: https://github.com/pytest-dev/pytest-mock/blob/master/LICENSE

4
_pytest_mock_version.py Normal file
View file

@ -0,0 +1,4 @@
# coding: utf-8
# file generated by setuptools_scm
# don't change, don't track in version control
version = '1.3.0'

7
appveyor.yml Normal file
View file

@ -0,0 +1,7 @@
install:
- C:\Python35\python -m pip install tox
build: false # Not a C# project
test_script:
- C:\Python35\scripts\tox

View file

@ -1,282 +1,284 @@
Metadata-Version: 1.1
Name: pytest-mock
Version: 1.2
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
Description: ===========
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
def test_unix_fs(mocker):
mocker.patch('os.remove')
UnixFS.rm('file')
os.remove.assert_called_once_with('file')
.. Using PNG badges because PyPI doesn't support SVG
|python| |version| |downloads| |ci| |appveyor| |coverage|
.. |version| image:: http://img.shields.io/pypi/v/pytest-mock.png
:target: https://pypi.python.org/pypi/pytest-mock
.. |downloads| image:: http://img.shields.io/pypi/dm/pytest-mock.png
:target: https://pypi.python.org/pypi/pytest-mock
.. |ci| image:: http://img.shields.io/travis/pytest-dev/pytest-mock.png
: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.png
: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/
Usage
=====
The ``mocker`` fixture has the same API as
`mock.patch <http://www.voidspace.org.uk/python/mock/patch.html#patch-decorators>`_,
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``: see http://www.voidspace.org.uk/python/mock/patch.html#patch.
* ``mocker.patch.object``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-object.
* ``mocker.patch.multiple``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-multiple.
* ``mocker.patch.dict``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-dict.
* ``mocker.stopall()``: stops all active patches up to this point.
* ``mocker.resetall()``: calls ``reset_mock()`` in all mocked objects up to this point.
Some 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>`_
* `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)*
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::
m = mocker.patch.object(DS, 'create_char')
DS().create_char('Raistlin', class_='mag', gift=12)
> m.assert_called_once_with('Raistlin', class_='mage', gift=12)
E assert {'class_': 'mag', 'gift': 12} == {'class_': 'mage', 'gift': 12}
E Omitting 1 identical items, use -v to show
E Differing items:
E {'class_': 'mag'} != {'class_': 'mage'}
E Use -v to get the full diff
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: https://pytest.org/latest/assert.html
Requirements
============
* Python 2.6+, Python 3.3+
* 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**
Although mocker's API is intentionally the same as ``mock.patch``'s, its uses as context managers and function decorators are **not** supported. 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__
License
=======
Distributed under the terms of the `MIT`_ license.
.. _MIT: https://github.com/pytest-dev/pytest-mock/blob/master/LICENSE
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.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Topic :: Software Development :: Testing
Metadata-Version: 1.1
Name: pytest-mock
Version: 1.3.0
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
Description: ===========
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
def test_unix_fs(mocker):
mocker.patch('os.remove')
UnixFS.rm('file')
os.remove.assert_called_once_with('file')
.. Using PNG badges because PyPI doesn't support SVG
|python| |version| |downloads| |ci| |appveyor| |coverage|
.. |version| image:: http://img.shields.io/pypi/v/pytest-mock.png
:target: https://pypi.python.org/pypi/pytest-mock
.. |downloads| image:: http://img.shields.io/pypi/dm/pytest-mock.png
:target: https://pypi.python.org/pypi/pytest-mock
.. |ci| image:: http://img.shields.io/travis/pytest-dev/pytest-mock.png
: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.png
: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/
Usage
=====
The ``mocker`` fixture has the same API as
`mock.patch <http://www.voidspace.org.uk/python/mock/patch.html#patch-decorators>`_,
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``: see http://www.voidspace.org.uk/python/mock/patch.html#patch.
* ``mocker.patch.object``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-object.
* ``mocker.patch.multiple``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-multiple.
* ``mocker.patch.dict``: see http://www.voidspace.org.uk/python/mock/patch.html#patch-dict.
* ``mocker.stopall()``: stops all active patches up to this point.
* ``mocker.resetall()``: calls ``reset_mock()`` in all mocked objects up to this point.
Some 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>`_
* `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::
m = mocker.patch.object(DS, 'create_char')
DS().create_char('Raistlin', class_='mag', gift=12)
> m.assert_called_once_with('Raistlin', class_='mage', gift=12)
E assert {'class_': 'mag', 'gift': 12} == {'class_': 'mage', 'gift': 12}
E Omitting 1 identical items, use -v to show
E Differing items:
E {'class_': 'mag'} != {'class_': 'mage'}
E Use -v to get the full diff
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: https://pytest.org/latest/assert.html
Requirements
============
* Python 2.6+, Python 3.3+
* 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**
Although mocker's API is intentionally the same as ``mock.patch``'s, its uses as context managers and function decorators are **not** supported. 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__
License
=======
Distributed under the terms of the `MIT`_ license.
.. _MIT: https://github.com/pytest-dev/pytest-mock/blob/master/LICENSE
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.6
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Topic :: Software Development :: Testing

View file

@ -1,10 +1,16 @@
.gitignore
.travis.yml
CHANGELOG.rst
LICENSE
MANIFEST.in
README.rst
_pytest_mock_version.py
appveyor.yml
pytest_mock.py
setup.cfg
setup.py
test_pytest_mock.py
tox.ini
pytest_mock.egg-info/PKG-INFO
pytest_mock.egg-info/SOURCES.txt
pytest_mock.egg-info/dependency_links.txt

View file

@ -1 +1,2 @@
_pytest_mock_version
pytest_mock

View file

@ -1,258 +1,264 @@
import inspect
import pytest
try:
import mock as mock_module
except ImportError:
import unittest.mock as mock_module
version = '1.2'
class MockFixture(object):
"""
Fixture that provides the same interface to functions in the mock module,
ensuring that they are uninstalled at the end of each test.
"""
Mock = mock_module.Mock
MagicMock = mock_module.MagicMock
PropertyMock = mock_module.PropertyMock
call = mock_module.call
ANY = mock_module.ANY
sentinel = mock_module.sentinel
def __init__(self):
self._patches = [] # list of mock._patch objects
self._mocks = [] # list of MagicMock objects
self.patch = self._Patcher(self._patches, self._mocks)
def resetall(self):
"""
Call reset_mock() on all patchers started by this fixture.
"""
for m in self._mocks:
m.reset_mock()
def stopall(self):
"""
Stop all patchers started by this fixture. Can be safely called multiple
times.
"""
for p in reversed(self._patches):
p.stop()
self._patches[:] = []
self._mocks[:] = []
def spy(self, obj, name):
"""
Creates a spy of method. It will run method normally, but it is now
possible to use `mock` call features with it, like call count.
:param object obj: An object.
:param unicode name: A method in object.
:rtype: mock.MagicMock
:return: Spy object.
"""
method = getattr(obj, name)
autospec = inspect.ismethod(method) or inspect.isfunction(method)
# Can't use autospec classmethod or staticmethod objects
# see: https://bugs.python.org/issue23078
if inspect.isclass(obj):
# Bypass class descriptor:
# http://stackoverflow.com/questions/14187973/python3-check-if-method-is-static
try:
value = obj.__getattribute__(obj, name)
except AttributeError:
pass
else:
if isinstance(value, (classmethod, staticmethod)):
autospec = False
result = self.patch.object(obj, name, side_effect=method,
autospec=autospec)
return result
def stub(self, name=None):
"""
Creates a stub method. It accepts any arguments. Ideal to register to
callbacks in tests.
:param name: the constructed stub's name as used in repr
:rtype: mock.MagicMock
:return: Stub object.
"""
return mock_module.MagicMock(spec=lambda *args, **kwargs: None, name=name)
class _Patcher(object):
"""
Object to provide the same interface as mock.patch, mock.patch.object,
etc. We need this indirection to keep the same API of the mock package.
"""
def __init__(self, patches, mocks):
self._patches = patches
self._mocks = mocks
def _start_patch(self, mock_func, *args, **kwargs):
"""Patches something by calling the given function from the mock
module, registering the patch to stop it later and returns the
mock object resulting from the mock call.
"""
p = mock_func(*args, **kwargs)
mocked = p.start()
self._patches.append(p)
self._mocks.append(mocked)
return mocked
def object(self, *args, **kwargs):
"""API to mock.patch.object"""
return self._start_patch(mock_module.patch.object, *args, **kwargs)
def multiple(self, *args, **kwargs):
"""API to mock.patch.multiple"""
return self._start_patch(mock_module.patch.multiple, *args,
**kwargs)
def dict(self, *args, **kwargs):
"""API to mock.patch.dict"""
return self._start_patch(mock_module.patch.dict, *args, **kwargs)
def __call__(self, *args, **kwargs):
"""API to mock.patch"""
return self._start_patch(mock_module.patch, *args, **kwargs)
@pytest.yield_fixture
def mocker():
"""
return an object that has the same interface to the `mock` module, but
takes care of automatically undoing all patches after each test method.
"""
result = MockFixture()
yield result
result.stopall()
@pytest.fixture
def mock(mocker):
"""
Same as "mocker", but kept only for backward compatibility.
"""
import warnings
warnings.warn('"mock" fixture has been deprecated, use "mocker" instead',
DeprecationWarning)
return mocker
_mock_module_patches = []
_mock_module_originals = {}
def assert_wrapper(__wrapped_mock_method__, *args, **kwargs):
__tracebackhide__ = True
try:
__wrapped_mock_method__(*args, **kwargs)
except AssertionError as e:
__mock_self = args[0]
if __mock_self.call_args is not None:
actual_args, actual_kwargs = __mock_self.call_args
assert actual_args == args[1:]
assert actual_kwargs == kwargs
raise AssertionError(*e.args)
def wrap_assert_not_called(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_not_called"],
*args, **kwargs)
def wrap_assert_called_with(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_called_with"],
*args, **kwargs)
def wrap_assert_called_once_with(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_called_once_with"],
*args, **kwargs)
def wrap_assert_has_calls(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_has_calls"],
*args, **kwargs)
def wrap_assert_any_call(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_any_call"],
*args, **kwargs)
def wrap_assert_methods(config):
"""
Wrap assert methods of mock module so we can hide their traceback and
add introspection information to specified argument asserts.
"""
# Make sure we only do this once
if _mock_module_originals:
return
wrappers = {
'assert_not_called': wrap_assert_not_called,
'assert_called_with': wrap_assert_called_with,
'assert_called_once_with': wrap_assert_called_once_with,
'assert_has_calls': wrap_assert_has_calls,
'assert_any_call': wrap_assert_any_call,
}
for method, wrapper in wrappers.items():
try:
original = getattr(mock_module.NonCallableMock, method)
except AttributeError:
continue
_mock_module_originals[method] = original
patcher = mock_module.patch.object(
mock_module.NonCallableMock, method, wrapper)
patcher.start()
_mock_module_patches.append(patcher)
if hasattr(config, 'add_cleanup'):
add_cleanup = config.add_cleanup
else:
# pytest 2.7 compatibility
add_cleanup = config._cleanup.append
add_cleanup(unwrap_assert_methods)
def unwrap_assert_methods():
for patcher in _mock_module_patches:
patcher.stop()
_mock_module_patches[:] = []
_mock_module_originals.clear()
def pytest_addoption(parser):
parser.addini('mock_traceback_monkeypatch',
'Monkeypatch the mock library to improve reporting of the '
'assert_called_... methods',
default=True)
def parse_ini_boolean(value):
if value in (True, False):
return value
try:
return {'true': True, 'false': False}[value.lower()]
except KeyError:
raise ValueError('unknown string for bool: %r' % value)
def pytest_configure(config):
tb = config.getoption('--tb')
if parse_ini_boolean(config.getini('mock_traceback_monkeypatch')) and tb != 'native':
wrap_assert_methods(config)
import inspect
import pytest
try:
import mock as mock_module
except ImportError:
import unittest.mock as mock_module
from _pytest_mock_version import version
__version__ = version
class MockFixture(object):
"""
Fixture that provides the same interface to functions in the mock module,
ensuring that they are uninstalled at the end of each test.
"""
Mock = mock_module.Mock
MagicMock = mock_module.MagicMock
PropertyMock = mock_module.PropertyMock
call = mock_module.call
ANY = mock_module.ANY
sentinel = mock_module.sentinel
mock_open = mock_module.mock_open
def __init__(self):
self._patches = [] # list of mock._patch objects
self._mocks = [] # list of MagicMock objects
self.patch = self._Patcher(self._patches, self._mocks)
# temporary fix: this should be at class level, but is blowing
# up in Python 3.6
self.sentinel = mock_module.sentinel
self.mock_open = mock_module.mock_open
def resetall(self):
"""
Call reset_mock() on all patchers started by this fixture.
"""
for m in self._mocks:
m.reset_mock()
def stopall(self):
"""
Stop all patchers started by this fixture. Can be safely called multiple
times.
"""
for p in reversed(self._patches):
p.stop()
self._patches[:] = []
self._mocks[:] = []
def spy(self, obj, name):
"""
Creates a spy of method. It will run method normally, but it is now
possible to use `mock` call features with it, like call count.
:param object obj: An object.
:param unicode name: A method in object.
:rtype: mock.MagicMock
:return: Spy object.
"""
method = getattr(obj, name)
autospec = inspect.ismethod(method) or inspect.isfunction(method)
# Can't use autospec classmethod or staticmethod objects
# see: https://bugs.python.org/issue23078
if inspect.isclass(obj):
# Bypass class descriptor:
# http://stackoverflow.com/questions/14187973/python3-check-if-method-is-static
try:
value = obj.__getattribute__(obj, name)
except AttributeError:
pass
else:
if isinstance(value, (classmethod, staticmethod)):
autospec = False
result = self.patch.object(obj, name, side_effect=method,
autospec=autospec)
return result
def stub(self, name=None):
"""
Creates a stub method. It accepts any arguments. Ideal to register to
callbacks in tests.
:param name: the constructed stub's name as used in repr
:rtype: mock.MagicMock
:return: Stub object.
"""
return mock_module.MagicMock(spec=lambda *args, **kwargs: None, name=name)
class _Patcher(object):
"""
Object to provide the same interface as mock.patch, mock.patch.object,
etc. We need this indirection to keep the same API of the mock package.
"""
def __init__(self, patches, mocks):
self._patches = patches
self._mocks = mocks
def _start_patch(self, mock_func, *args, **kwargs):
"""Patches something by calling the given function from the mock
module, registering the patch to stop it later and returns the
mock object resulting from the mock call.
"""
p = mock_func(*args, **kwargs)
mocked = p.start()
self._patches.append(p)
self._mocks.append(mocked)
return mocked
def object(self, *args, **kwargs):
"""API to mock.patch.object"""
return self._start_patch(mock_module.patch.object, *args, **kwargs)
def multiple(self, *args, **kwargs):
"""API to mock.patch.multiple"""
return self._start_patch(mock_module.patch.multiple, *args,
**kwargs)
def dict(self, *args, **kwargs):
"""API to mock.patch.dict"""
return self._start_patch(mock_module.patch.dict, *args, **kwargs)
def __call__(self, *args, **kwargs):
"""API to mock.patch"""
return self._start_patch(mock_module.patch, *args, **kwargs)
@pytest.yield_fixture
def mocker():
"""
return an object that has the same interface to the `mock` module, but
takes care of automatically undoing all patches after each test method.
"""
result = MockFixture()
yield result
result.stopall()
@pytest.fixture
def mock(mocker):
"""
Same as "mocker", but kept only for backward compatibility.
"""
import warnings
warnings.warn('"mock" fixture has been deprecated, use "mocker" instead',
DeprecationWarning)
return mocker
_mock_module_patches = []
_mock_module_originals = {}
def assert_wrapper(__wrapped_mock_method__, *args, **kwargs):
__tracebackhide__ = True
try:
__wrapped_mock_method__(*args, **kwargs)
except AssertionError as e:
__mock_self = args[0]
if __mock_self.call_args is not None:
actual_args, actual_kwargs = __mock_self.call_args
assert actual_args == args[1:]
assert actual_kwargs == kwargs
raise AssertionError(*e.args)
def wrap_assert_not_called(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_not_called"],
*args, **kwargs)
def wrap_assert_called_with(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_called_with"],
*args, **kwargs)
def wrap_assert_called_once_with(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_called_once_with"],
*args, **kwargs)
def wrap_assert_has_calls(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_has_calls"],
*args, **kwargs)
def wrap_assert_any_call(*args, **kwargs):
__tracebackhide__ = True
assert_wrapper(_mock_module_originals["assert_any_call"],
*args, **kwargs)
def wrap_assert_methods(config):
"""
Wrap assert methods of mock module so we can hide their traceback and
add introspection information to specified argument asserts.
"""
# Make sure we only do this once
if _mock_module_originals:
return
wrappers = {
'assert_not_called': wrap_assert_not_called,
'assert_called_with': wrap_assert_called_with,
'assert_called_once_with': wrap_assert_called_once_with,
'assert_has_calls': wrap_assert_has_calls,
'assert_any_call': wrap_assert_any_call,
}
for method, wrapper in wrappers.items():
try:
original = getattr(mock_module.NonCallableMock, method)
except AttributeError: # pragma: no cover
continue
_mock_module_originals[method] = original
patcher = mock_module.patch.object(
mock_module.NonCallableMock, method, wrapper)
patcher.start()
_mock_module_patches.append(patcher)
if hasattr(config, 'add_cleanup'):
add_cleanup = config.add_cleanup
else:
# pytest 2.7 compatibility
add_cleanup = config._cleanup.append
add_cleanup(unwrap_assert_methods)
def unwrap_assert_methods():
for patcher in _mock_module_patches:
patcher.stop()
_mock_module_patches[:] = []
_mock_module_originals.clear()
def pytest_addoption(parser):
parser.addini('mock_traceback_monkeypatch',
'Monkeypatch the mock library to improve reporting of the '
'assert_called_... methods',
default=True)
def parse_ini_boolean(value):
if value in (True, False):
return value
try:
return {'true': True, 'false': False}[value.lower()]
except KeyError:
raise ValueError('unknown string for bool: %r' % value)
def pytest_configure(config):
tb = config.getoption('--tb')
if parse_ini_boolean(config.getini('mock_traceback_monkeypatch')) and tb != 'native':
wrap_assert_methods(config)

View file

@ -1,8 +1,8 @@
[bdist_wheel]
universal = 1
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0
[bdist_wheel]
universal = 1
[egg_info]
tag_build =
tag_date = 0
tag_svn_revision = 0

View file

@ -1,45 +1,43 @@
import re
from setuptools import setup
with open('pytest_mock.py') as f:
m = re.search("version = '(.*)'", f.read())
assert m is not None
version = m.group(1)
setup(
name='pytest-mock',
version=version,
entry_points={
'pytest11': ['pytest_mock = pytest_mock'],
},
py_modules=['pytest_mock'],
platforms='any',
install_requires=[
'pytest>=2.7',
],
extras_require={
':python_version=="2.6" or python_version=="2.7"': ['mock'],
},
url='https://github.com/pytest-dev/pytest-mock/',
license='MIT',
author='Bruno Oliveira',
author_email='nicoddemus@gmail.com',
description='Thin-wrapper around the mock package for easier use with py.test',
long_description=open('README.rst').read(),
keywords="pytest mock",
classifiers=[
'Development Status :: 5 - Production/Stable',
'Framework :: Pytest',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Topic :: Software Development :: Testing',
]
)
import re
from setuptools import setup
setup(
name='pytest-mock',
entry_points={
'pytest11': ['pytest_mock = pytest_mock'],
},
py_modules=['pytest_mock', '_pytest_mock_version'],
platforms='any',
install_requires=[
'pytest>=2.7',
],
extras_require={
':python_version=="2.6" or python_version=="2.7"': ['mock'],
},
use_scm_version={'write_to': '_pytest_mock_version.py'},
setup_requires=['setuptools_scm'],
url='https://github.com/pytest-dev/pytest-mock/',
license='MIT',
author='Bruno Oliveira',
author_email='nicoddemus@gmail.com',
description='Thin-wrapper around the mock package for easier use with py.test',
long_description=open('README.rst').read(),
keywords="pytest mock",
classifiers=[
'Development Status :: 5 - Production/Stable',
'Framework :: Pytest',
'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License',
'Operating System :: OS Independent',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Topic :: Software Development :: Testing',
]
)

View file

@ -1,500 +1,500 @@
import os
import platform
import sys
from contextlib import contextmanager
import py.code
import pytest
pytest_plugins = 'pytester'
# could not make some of the tests work on PyPy, patches are welcome!
skip_pypy = pytest.mark.skipif(platform.python_implementation() == 'PyPy',
reason='could not make work on pypy')
class UnixFS(object):
"""
Wrapper to os functions to simulate a Unix file system, used for testing
the mock fixture.
"""
@classmethod
def rm(cls, filename):
os.remove(filename)
@classmethod
def ls(cls, path):
return os.listdir(path)
@pytest.fixture
def check_unix_fs_mocked(tmpdir, mocker):
"""
performs a standard test in a UnixFS, assuming that both `os.remove` and
`os.listdir` have been mocked previously.
"""
def check(mocked_rm, mocked_ls):
assert mocked_rm is os.remove
assert mocked_ls is os.listdir
file_name = tmpdir / 'foo.txt'
file_name.ensure()
UnixFS.rm(str(file_name))
mocked_rm.assert_called_once_with(str(file_name))
assert os.path.isfile(str(file_name))
mocked_ls.return_value = ['bar.txt']
assert UnixFS.ls(str(tmpdir)) == ['bar.txt']
mocked_ls.assert_called_once_with(str(tmpdir))
mocker.stopall()
assert UnixFS.ls(str(tmpdir)) == ['foo.txt']
UnixFS.rm(str(file_name))
assert not os.path.isfile(str(file_name))
return check
def mock_using_patch_object(mocker):
return mocker.patch.object(os, 'remove'), mocker.patch.object(os, 'listdir')
def mock_using_patch(mocker):
return mocker.patch('os.remove'), mocker.patch('os.listdir')
def mock_using_patch_multiple(mocker):
from pytest_mock import mock_module
r = mocker.patch.multiple('os', remove=mock_module.DEFAULT,
listdir=mock_module.DEFAULT)
return r['remove'], r['listdir']
@pytest.mark.parametrize('mock_fs', [mock_using_patch_object, mock_using_patch,
mock_using_patch_multiple],
)
def test_mock_patches(mock_fs, mocker, check_unix_fs_mocked):
"""
Installs mocks into `os` functions and performs a standard testing of
mock functionality. We parametrize different mock methods to ensure
all (intended, at least) mock API is covered.
"""
# mock it twice on purpose to ensure we unmock it correctly later
mock_fs(mocker)
mocked_rm, mocked_ls = mock_fs(mocker)
check_unix_fs_mocked(mocked_rm, mocked_ls)
def test_mock_patch_dict(mocker):
"""
Testing
:param mock:
"""
x = {'original': 1}
mocker.patch.dict(x, values=[('new', 10)], clear=True)
assert x == {'new': 10}
mocker.stopall()
assert x == {'original': 1}
def test_mock_fixture_is_deprecated(testdir):
"""
Test that a warning emitted when using deprecated "mock" fixture.
"""
testdir.makepyfile('''
import warnings
import os
warnings.simplefilter('always')
def test_foo(mock, tmpdir):
mock.patch('os.listdir', return_value=['mocked'])
assert os.listdir(str(tmpdir)) == ['mocked']
mock.stopall()
assert os.listdir(str(tmpdir)) == []
''')
result = testdir.runpytest('-s')
result.stderr.fnmatch_lines(['*"mock" fixture has been deprecated*'])
def test_deprecated_mock(mock, tmpdir):
"""
Use backward-compatibility-only mock fixture to ensure complete coverage.
"""
mock.patch('os.listdir', return_value=['mocked'])
assert os.listdir(str(tmpdir)) == ['mocked']
mock.stopall()
assert os.listdir(str(tmpdir)) == []
@pytest.mark.parametrize('name', ['MagicMock', 'PropertyMock', 'Mock', 'call', 'ANY', 'sentinel'])
def test_mocker_aliases(name):
from pytest_mock import mock_module, MockFixture
mocker = MockFixture()
assert getattr(mocker, name) is getattr(mock_module, name)
def test_mocker_resetall(mocker):
listdir = mocker.patch('os.listdir')
open = mocker.patch('os.open')
listdir("/tmp")
open("/tmp/foo.txt")
listdir.assert_called_once_with("/tmp")
open.assert_called_once_with("/tmp/foo.txt")
mocker.resetall()
assert not listdir.called
assert not open.called
class TestMockerStub:
def test_call(self, mocker):
stub = mocker.stub()
stub('foo', 'bar')
stub.assert_called_once_with('foo', 'bar')
def test_repr_with_no_name(self, mocker):
stub = mocker.stub()
assert not 'name' in repr(stub)
def test_repr_with_name(self, mocker):
test_name = 'funny walk'
stub = mocker.stub(name=test_name)
assert "name={0!r}".format(test_name) in repr(stub)
def __test_failure_message(self, mocker, **kwargs):
expected_name = kwargs.get('name') or 'mock'
expected_message = 'Expected call: {0}()\nNot called'.format(expected_name)
stub = mocker.stub(**kwargs)
with pytest.raises(AssertionError) as exc_info:
stub.assert_called_with()
assert exc_info.value.msg == expected_message
def test_failure_message_with_no_name(self, mocker):
self.__test_failure_message(mocker)
@pytest.mark.parametrize('name', (None, '', 'f', 'The Castle of aaarrrrggh'))
def test_failure_message_with_name(self, mocker, name):
self.__test_failure_message(mocker, name=name)
def test_instance_method_spy(mocker):
class Foo(object):
def bar(self, arg):
return arg * 2
foo = Foo()
other = Foo()
spy = mocker.spy(foo, 'bar')
assert foo.bar(arg=10) == 20
assert other.bar(arg=10) == 20
foo.bar.assert_called_once_with(arg=10)
spy.assert_called_once_with(arg=10)
@skip_pypy
def test_instance_method_by_class_spy(mocker):
from pytest_mock import mock_module
class Foo(object):
def bar(self, arg):
return arg * 2
spy = mocker.spy(Foo, 'bar')
foo = Foo()
other = Foo()
assert foo.bar(arg=10) == 20
assert other.bar(arg=10) == 20
calls = [mock_module.call(foo, arg=10), mock_module.call(other, arg=10)]
assert spy.call_args_list == calls
@skip_pypy
def test_instance_method_by_subclass_spy(mocker):
from pytest_mock import mock_module
class Base(object):
def bar(self, arg):
return arg * 2
class Foo(Base):
pass
spy = mocker.spy(Foo, 'bar')
foo = Foo()
other = Foo()
assert foo.bar(arg=10) == 20
assert other.bar(arg=10) == 20
calls = [mock_module.call(foo, arg=10), mock_module.call(other, arg=10)]
assert spy.call_args_list == calls
@skip_pypy
def test_class_method_spy(mocker):
class Foo(object):
@classmethod
def bar(cls, arg):
return arg * 2
spy = mocker.spy(Foo, 'bar')
assert Foo.bar(arg=10) == 20
Foo.bar.assert_called_once_with(arg=10)
spy.assert_called_once_with(arg=10)
@skip_pypy
@pytest.mark.xfail(sys.version_info[0] == 2, reason='does not work on Python 2')
def test_class_method_subclass_spy(mocker):
class Base(object):
@classmethod
def bar(self, arg):
return arg * 2
class Foo(Base):
pass
spy = mocker.spy(Foo, 'bar')
assert Foo.bar(arg=10) == 20
Foo.bar.assert_called_once_with(arg=10)
spy.assert_called_once_with(arg=10)
@skip_pypy
def test_class_method_with_metaclass_spy(mocker):
class MetaFoo(type):
pass
class Foo(object):
__metaclass__ = MetaFoo
@classmethod
def bar(cls, arg):
return arg * 2
spy = mocker.spy(Foo, 'bar')
assert Foo.bar(arg=10) == 20
Foo.bar.assert_called_once_with(arg=10)
spy.assert_called_once_with(arg=10)
@skip_pypy
def test_static_method_spy(mocker):
class Foo(object):
@staticmethod
def bar(arg):
return arg * 2
spy = mocker.spy(Foo, 'bar')
assert Foo.bar(arg=10) == 20
Foo.bar.assert_called_once_with(arg=10)
spy.assert_called_once_with(arg=10)
@skip_pypy
@pytest.mark.xfail(sys.version_info[0] == 2, reason='does not work on Python 2')
def test_static_method_subclass_spy(mocker):
class Base(object):
@staticmethod
def bar(arg):
return arg * 2
class Foo(Base):
pass
spy = mocker.spy(Foo, 'bar')
assert Foo.bar(arg=10) == 20
Foo.bar.assert_called_once_with(arg=10)
spy.assert_called_once_with(arg=10)
@contextmanager
def assert_traceback():
"""
Assert that this file is at the top of the filtered traceback
"""
try:
yield
except AssertionError:
traceback = py.code.ExceptionInfo().traceback
crashentry = traceback.getcrashentry()
assert crashentry.path == __file__
else:
raise AssertionError("DID NOT RAISE")
@contextmanager
def assert_argument_introspection(left, right):
"""
Assert detailed argument introspection is used
"""
try:
yield
except AssertionError as e:
# this may be a bit too assuming, but seems nicer then hard-coding
import _pytest.assertion.util as util
# NOTE: we assert with either verbose or not, depending on how our own
# test was run by examining sys.argv
verbose = any(a.startswith('-v') for a in sys.argv)
expected = '\n '.join(util._compare_eq_iterable(left, right, verbose))
assert expected in e.msg
else:
raise AssertionError("DID NOT RAISE")
@pytest.mark.skipif(sys.version_info[:2] in [(3, 3), (3, 4)],
reason="assert_not_called not available in python 3.3 and 3.4")
def test_assert_not_called_wrapper(mocker):
stub = mocker.stub()
stub.assert_not_called()
stub()
with assert_traceback():
stub.assert_not_called()
def test_assert_called_with_wrapper(mocker):
stub = mocker.stub()
stub("foo")
stub.assert_called_with("foo")
with assert_traceback():
stub.assert_called_with("bar")
def test_assert_called_once_with_wrapper(mocker):
stub = mocker.stub()
stub("foo")
stub.assert_called_once_with("foo")
stub("foo")
with assert_traceback():
stub.assert_called_once_with("foo")
def test_assert_called_args_with_introspection(mocker):
stub = mocker.stub()
complex_args = ('a', 1, set(['test']))
wrong_args = ('b', 2, set(['jest']))
stub(*complex_args)
stub.assert_called_with(*complex_args)
stub.assert_called_once_with(*complex_args)
with assert_argument_introspection(complex_args, wrong_args):
stub.assert_called_with(*wrong_args)
stub.assert_called_once_with(*wrong_args)
def test_assert_called_kwargs_with_introspection(mocker):
stub = mocker.stub()
complex_kwargs = dict(foo={'bar': 1, 'baz': 'spam'})
wrong_kwargs = dict(foo={'goo': 1, 'baz': 'bran'})
stub(**complex_kwargs)
stub.assert_called_with(**complex_kwargs)
stub.assert_called_once_with(**complex_kwargs)
with assert_argument_introspection(complex_kwargs, wrong_kwargs):
stub.assert_called_with(**wrong_kwargs)
stub.assert_called_once_with(**wrong_kwargs)
def test_assert_any_call_wrapper(mocker):
stub = mocker.stub()
stub("foo")
stub("foo")
stub.assert_any_call("foo")
with assert_traceback():
stub.assert_any_call("bar")
def test_assert_has_calls(mocker):
from pytest_mock import mock_module
stub = mocker.stub()
stub("foo")
stub.assert_has_calls([mock_module.call("foo")])
with assert_traceback():
stub.assert_has_calls([mock_module.call("bar")])
def test_monkeypatch_ini(mocker, testdir):
# Make sure the following function actually tests something
stub = mocker.stub()
assert stub.assert_called_with.__module__ != stub.__module__
testdir.makepyfile("""
import py.code
def test_foo(mocker):
stub = mocker.stub()
assert stub.assert_called_with.__module__ == stub.__module__
""")
testdir.makeini("""
[pytest]
mock_traceback_monkeypatch = false
""")
if hasattr(testdir, 'runpytest_subprocess'):
result = testdir.runpytest_subprocess()
else:
# pytest 2.7.X
result = testdir.runpytest()
assert result.ret == 0
def test_parse_ini_boolean():
import pytest_mock
assert pytest_mock.parse_ini_boolean('True') is True
assert pytest_mock.parse_ini_boolean('false') is False
with pytest.raises(ValueError):
pytest_mock.parse_ini_boolean('foo')
def test_patched_method_parameter_name(mocker):
"""Test that our internal code uses uncommon names when wrapping other
"mock" methods to avoid conflicts with user code (#31).
"""
class Request:
@classmethod
def request(cls, method, args):
pass
m = mocker.patch.object(Request, 'request')
Request.request(method='get', args={'type': 'application/json'})
m.assert_called_once_with(method='get', args={'type': 'application/json'})
def test_monkeypatch_native(testdir):
"""Automatically disable monkeypatching when --tb=native.
"""
testdir.makepyfile("""
def test_foo(mocker):
stub = mocker.stub()
stub(1, greet='hello')
stub.assert_called_once_with(1, greet='hey')
""")
if hasattr(testdir, 'runpytest_subprocess'):
result = testdir.runpytest_subprocess('--tb=native')
else:
# pytest 2.7.X
result = testdir.runpytest('--tb=native')
assert result.ret == 1
assert 'During handling of the above exception' not in result.stdout.str()
assert 'Differing items:' not in result.stdout.str()
traceback_lines = [x for x in result.stdout.str().splitlines()
if 'Traceback (most recent call last)' in x]
assert len(traceback_lines) == 1 # make sure there are no duplicated tracebacks (#44)
import os
import platform
import sys
from contextlib import contextmanager
import py.code
import pytest
pytest_plugins = 'pytester'
# could not make some of the tests work on PyPy, patches are welcome!
skip_pypy = pytest.mark.skipif(platform.python_implementation() == 'PyPy',
reason='could not make work on pypy')
class UnixFS(object):
"""
Wrapper to os functions to simulate a Unix file system, used for testing
the mock fixture.
"""
@classmethod
def rm(cls, filename):
os.remove(filename)
@classmethod
def ls(cls, path):
return os.listdir(path)
@pytest.fixture
def check_unix_fs_mocked(tmpdir, mocker):
"""
performs a standard test in a UnixFS, assuming that both `os.remove` and
`os.listdir` have been mocked previously.
"""
def check(mocked_rm, mocked_ls):
assert mocked_rm is os.remove
assert mocked_ls is os.listdir
file_name = tmpdir / 'foo.txt'
file_name.ensure()
UnixFS.rm(str(file_name))
mocked_rm.assert_called_once_with(str(file_name))
assert os.path.isfile(str(file_name))
mocked_ls.return_value = ['bar.txt']
assert UnixFS.ls(str(tmpdir)) == ['bar.txt']
mocked_ls.assert_called_once_with(str(tmpdir))
mocker.stopall()
assert UnixFS.ls(str(tmpdir)) == ['foo.txt']
UnixFS.rm(str(file_name))
assert not os.path.isfile(str(file_name))
return check
def mock_using_patch_object(mocker):
return mocker.patch.object(os, 'remove'), mocker.patch.object(os, 'listdir')
def mock_using_patch(mocker):
return mocker.patch('os.remove'), mocker.patch('os.listdir')
def mock_using_patch_multiple(mocker):
from pytest_mock import mock_module
r = mocker.patch.multiple('os', remove=mock_module.DEFAULT,
listdir=mock_module.DEFAULT)
return r['remove'], r['listdir']
@pytest.mark.parametrize('mock_fs', [mock_using_patch_object, mock_using_patch,
mock_using_patch_multiple],
)
def test_mock_patches(mock_fs, mocker, check_unix_fs_mocked):
"""
Installs mocks into `os` functions and performs a standard testing of
mock functionality. We parametrize different mock methods to ensure
all (intended, at least) mock API is covered.
"""
# mock it twice on purpose to ensure we unmock it correctly later
mock_fs(mocker)
mocked_rm, mocked_ls = mock_fs(mocker)
check_unix_fs_mocked(mocked_rm, mocked_ls)
def test_mock_patch_dict(mocker):
"""
Testing
:param mock:
"""
x = {'original': 1}
mocker.patch.dict(x, values=[('new', 10)], clear=True)
assert x == {'new': 10}
mocker.stopall()
assert x == {'original': 1}
def test_mock_fixture_is_deprecated(testdir):
"""
Test that a warning emitted when using deprecated "mock" fixture.
"""
testdir.makepyfile('''
import warnings
import os
warnings.simplefilter('always')
def test_foo(mock, tmpdir):
mock.patch('os.listdir', return_value=['mocked'])
assert os.listdir(str(tmpdir)) == ['mocked']
mock.stopall()
assert os.listdir(str(tmpdir)) == []
''')
result = testdir.runpytest('-s')
result.stderr.fnmatch_lines(['*"mock" fixture has been deprecated*'])
def test_deprecated_mock(mock, tmpdir):
"""
Use backward-compatibility-only mock fixture to ensure complete coverage.
"""
mock.patch('os.listdir', return_value=['mocked'])
assert os.listdir(str(tmpdir)) == ['mocked']
mock.stopall()
assert os.listdir(str(tmpdir)) == []
@pytest.mark.parametrize('name', ['MagicMock', 'PropertyMock', 'Mock', 'call', 'ANY', 'sentinel', 'mock_open'])
def test_mocker_aliases(name):
from pytest_mock import mock_module, MockFixture
mocker = MockFixture()
assert getattr(mocker, name) is getattr(mock_module, name)
def test_mocker_resetall(mocker):
listdir = mocker.patch('os.listdir')
open = mocker.patch('os.open')
listdir("/tmp")
open("/tmp/foo.txt")
listdir.assert_called_once_with("/tmp")
open.assert_called_once_with("/tmp/foo.txt")
mocker.resetall()
assert not listdir.called
assert not open.called
class TestMockerStub:
def test_call(self, mocker):
stub = mocker.stub()
stub('foo', 'bar')
stub.assert_called_once_with('foo', 'bar')
def test_repr_with_no_name(self, mocker):
stub = mocker.stub()
assert not 'name' in repr(stub)
def test_repr_with_name(self, mocker):
test_name = 'funny walk'
stub = mocker.stub(name=test_name)
assert "name={0!r}".format(test_name) in repr(stub)
def __test_failure_message(self, mocker, **kwargs):
expected_name = kwargs.get('name') or 'mock'
expected_message = 'Expected call: {0}()\nNot called'.format(expected_name)
stub = mocker.stub(**kwargs)
with pytest.raises(AssertionError) as exc_info:
stub.assert_called_with()
assert str(exc_info.value) == expected_message
def test_failure_message_with_no_name(self, mocker):
self.__test_failure_message(mocker)
@pytest.mark.parametrize('name', (None, '', 'f', 'The Castle of aaarrrrggh'))
def test_failure_message_with_name(self, mocker, name):
self.__test_failure_message(mocker, name=name)
def test_instance_method_spy(mocker):
class Foo(object):
def bar(self, arg):
return arg * 2
foo = Foo()
other = Foo()
spy = mocker.spy(foo, 'bar')
assert foo.bar(arg=10) == 20
assert other.bar(arg=10) == 20
foo.bar.assert_called_once_with(arg=10)
spy.assert_called_once_with(arg=10)
@skip_pypy
def test_instance_method_by_class_spy(mocker):
from pytest_mock import mock_module
class Foo(object):
def bar(self, arg):
return arg * 2
spy = mocker.spy(Foo, 'bar')
foo = Foo()
other = Foo()
assert foo.bar(arg=10) == 20
assert other.bar(arg=10) == 20
calls = [mock_module.call(foo, arg=10), mock_module.call(other, arg=10)]
assert spy.call_args_list == calls
@skip_pypy
def test_instance_method_by_subclass_spy(mocker):
from pytest_mock import mock_module
class Base(object):
def bar(self, arg):
return arg * 2
class Foo(Base):
pass
spy = mocker.spy(Foo, 'bar')
foo = Foo()
other = Foo()
assert foo.bar(arg=10) == 20
assert other.bar(arg=10) == 20
calls = [mock_module.call(foo, arg=10), mock_module.call(other, arg=10)]
assert spy.call_args_list == calls
@skip_pypy
def test_class_method_spy(mocker):
class Foo(object):
@classmethod
def bar(cls, arg):
return arg * 2
spy = mocker.spy(Foo, 'bar')
assert Foo.bar(arg=10) == 20
Foo.bar.assert_called_once_with(arg=10)
spy.assert_called_once_with(arg=10)
@skip_pypy
@pytest.mark.xfail(sys.version_info[0] == 2, reason='does not work on Python 2')
def test_class_method_subclass_spy(mocker):
class Base(object):
@classmethod
def bar(self, arg):
return arg * 2
class Foo(Base):
pass
spy = mocker.spy(Foo, 'bar')
assert Foo.bar(arg=10) == 20
Foo.bar.assert_called_once_with(arg=10)
spy.assert_called_once_with(arg=10)
@skip_pypy
def test_class_method_with_metaclass_spy(mocker):
class MetaFoo(type):
pass
class Foo(object):
__metaclass__ = MetaFoo
@classmethod
def bar(cls, arg):
return arg * 2
spy = mocker.spy(Foo, 'bar')
assert Foo.bar(arg=10) == 20
Foo.bar.assert_called_once_with(arg=10)
spy.assert_called_once_with(arg=10)
@skip_pypy
def test_static_method_spy(mocker):
class Foo(object):
@staticmethod
def bar(arg):
return arg * 2
spy = mocker.spy(Foo, 'bar')
assert Foo.bar(arg=10) == 20
Foo.bar.assert_called_once_with(arg=10)
spy.assert_called_once_with(arg=10)
@skip_pypy
@pytest.mark.xfail(sys.version_info[0] == 2, reason='does not work on Python 2')
def test_static_method_subclass_spy(mocker):
class Base(object):
@staticmethod
def bar(arg):
return arg * 2
class Foo(Base):
pass
spy = mocker.spy(Foo, 'bar')
assert Foo.bar(arg=10) == 20
Foo.bar.assert_called_once_with(arg=10)
spy.assert_called_once_with(arg=10)
@contextmanager
def assert_traceback():
"""
Assert that this file is at the top of the filtered traceback
"""
try:
yield
except AssertionError:
traceback = py.code.ExceptionInfo().traceback
crashentry = traceback.getcrashentry()
assert crashentry.path == __file__
else:
raise AssertionError("DID NOT RAISE")
@contextmanager
def assert_argument_introspection(left, right):
"""
Assert detailed argument introspection is used
"""
try:
yield
except AssertionError as e:
# this may be a bit too assuming, but seems nicer then hard-coding
import _pytest.assertion.util as util
# NOTE: we assert with either verbose or not, depending on how our own
# test was run by examining sys.argv
verbose = any(a.startswith('-v') for a in sys.argv)
expected = '\n '.join(util._compare_eq_iterable(left, right, verbose))
assert expected in str(e)
else:
raise AssertionError("DID NOT RAISE")
@pytest.mark.skipif(sys.version_info[:2] in [(3, 3), (3, 4)],
reason="assert_not_called not available in python 3.3 and 3.4")
def test_assert_not_called_wrapper(mocker):
stub = mocker.stub()
stub.assert_not_called()
stub()
with assert_traceback():
stub.assert_not_called()
def test_assert_called_with_wrapper(mocker):
stub = mocker.stub()
stub("foo")
stub.assert_called_with("foo")
with assert_traceback():
stub.assert_called_with("bar")
def test_assert_called_once_with_wrapper(mocker):
stub = mocker.stub()
stub("foo")
stub.assert_called_once_with("foo")
stub("foo")
with assert_traceback():
stub.assert_called_once_with("foo")
def test_assert_called_args_with_introspection(mocker):
stub = mocker.stub()
complex_args = ('a', 1, set(['test']))
wrong_args = ('b', 2, set(['jest']))
stub(*complex_args)
stub.assert_called_with(*complex_args)
stub.assert_called_once_with(*complex_args)
with assert_argument_introspection(complex_args, wrong_args):
stub.assert_called_with(*wrong_args)
stub.assert_called_once_with(*wrong_args)
def test_assert_called_kwargs_with_introspection(mocker):
stub = mocker.stub()
complex_kwargs = dict(foo={'bar': 1, 'baz': 'spam'})
wrong_kwargs = dict(foo={'goo': 1, 'baz': 'bran'})
stub(**complex_kwargs)
stub.assert_called_with(**complex_kwargs)
stub.assert_called_once_with(**complex_kwargs)
with assert_argument_introspection(complex_kwargs, wrong_kwargs):
stub.assert_called_with(**wrong_kwargs)
stub.assert_called_once_with(**wrong_kwargs)
def test_assert_any_call_wrapper(mocker):
stub = mocker.stub()
stub("foo")
stub("foo")
stub.assert_any_call("foo")
with assert_traceback():
stub.assert_any_call("bar")
def test_assert_has_calls(mocker):
from pytest_mock import mock_module
stub = mocker.stub()
stub("foo")
stub.assert_has_calls([mock_module.call("foo")])
with assert_traceback():
stub.assert_has_calls([mock_module.call("bar")])
def test_monkeypatch_ini(mocker, testdir):
# Make sure the following function actually tests something
stub = mocker.stub()
assert stub.assert_called_with.__module__ != stub.__module__
testdir.makepyfile("""
import py.code
def test_foo(mocker):
stub = mocker.stub()
assert stub.assert_called_with.__module__ == stub.__module__
""")
testdir.makeini("""
[pytest]
mock_traceback_monkeypatch = false
""")
if hasattr(testdir, 'runpytest_subprocess'):
result = testdir.runpytest_subprocess()
else:
# pytest 2.7.X
result = testdir.runpytest()
assert result.ret == 0
def test_parse_ini_boolean():
import pytest_mock
assert pytest_mock.parse_ini_boolean('True') is True
assert pytest_mock.parse_ini_boolean('false') is False
with pytest.raises(ValueError):
pytest_mock.parse_ini_boolean('foo')
def test_patched_method_parameter_name(mocker):
"""Test that our internal code uses uncommon names when wrapping other
"mock" methods to avoid conflicts with user code (#31).
"""
class Request:
@classmethod
def request(cls, method, args):
pass
m = mocker.patch.object(Request, 'request')
Request.request(method='get', args={'type': 'application/json'})
m.assert_called_once_with(method='get', args={'type': 'application/json'})
def test_monkeypatch_native(testdir):
"""Automatically disable monkeypatching when --tb=native.
"""
testdir.makepyfile("""
def test_foo(mocker):
stub = mocker.stub()
stub(1, greet='hello')
stub.assert_called_once_with(1, greet='hey')
""")
if hasattr(testdir, 'runpytest_subprocess'):
result = testdir.runpytest_subprocess('--tb=native')
else:
# pytest 2.7.X
result = testdir.runpytest('--tb=native')
assert result.ret == 1
assert 'During handling of the above exception' not in result.stdout.str()
assert 'Differing items:' not in result.stdout.str()
traceback_lines = [x for x in result.stdout.str().splitlines()
if 'Traceback (most recent call last)' in x]
assert len(traceback_lines) == 1 # make sure there are no duplicated tracebacks (#44)

24
tox.ini Normal file
View file

@ -0,0 +1,24 @@
[tox]
envlist = py{26,27,33,34,35}-pytest{27,28,29,30},linting
[testenv]
passenv = USER USERNAME
deps =
coverage
pytest27: pytest~=2.7
pytest28: pytest~=2.8
pytest29: pytest~=2.9
pytest30: pytest~=3.0
commands =
coverage run --append --source=pytest_mock.py -m pytest test_pytest_mock.py
[testenv:linting]
basepython = python3.5
skip_install=True
deps =
pytest-flakes
restructuredtext_lint
pygments
commands =
py.test --flakes pytest_mock.py test_pytest_mock.py -m flakes
rst-lint CHANGELOG.rst README.rst