pytest-mock-1.7.1/0000775000372000037200000000000013245755645014661 5ustar travistravis00000000000000pytest-mock-1.7.1/README.rst0000664000372000037200000002436013245755624016352 0ustar travistravis00000000000000=========== pytest-mock =========== This plugin installs a ``mocker`` fixture which is a thin-wrapper around the patching API provided by the `mock package `_, but with the benefit of not having to worry about undoing patches at the end of a test: .. code-block:: python import os class UnixFS: @staticmethod def rm(filename): os.remove(filename) def test_unix_fs(mocker): mocker.patch('os.remove') UnixFS.rm('file') os.remove.assert_called_once_with('file') .. Using PNG badges because PyPI doesn't support SVG |python| |version| |anaconda| |ci| |appveyor| |coverage| .. |version| image:: http://img.shields.io/pypi/v/pytest-mock.svg :target: https://pypi.python.org/pypi/pytest-mock .. |anaconda| image:: https://anaconda.org/conda-forge/pytest-mock/badges/version.svg :target: https://anaconda.org/conda-forge/pytest-mock .. |ci| image:: http://img.shields.io/travis/pytest-dev/pytest-mock.svg :target: https://travis-ci.org/pytest-dev/pytest-mock .. |appveyor| image:: https://ci.appveyor.com/api/projects/status/pid1t7iuwhkm9eh6/branch/master?svg=true :target: https://ci.appveyor.com/project/pytestbot/pytest-mock .. |coverage| image:: http://img.shields.io/coveralls/pytest-dev/pytest-mock.svg :target: https://coveralls.io/r/pytest-dev/pytest-mock .. |python| image:: https://img.shields.io/pypi/pyversions/pytest-mock.svg :target: https://pypi.python.org/pypi/pytest-mock/ .. image:: http://www.opensourcecitizen.org/badge?url=github.com/pytest-dev/pytest-mock :target: http://www.opensourcecitizen.org/project?url=github.com/pytest-dev/pytest-mock If you found this library useful, donate some CPU cycles to its development efforts by clicking above. Thank you! 😇 Usage ===== The ``mocker`` fixture has the same API as `mock.patch `_, supporting the same arguments: .. code-block:: python def test_foo(mocker): # all valid calls mocker.patch('os.remove') mocker.patch.object(os, 'listdir', autospec=True) mocked_isfile = mocker.patch('os.path.isfile') The supported methods are: * ``mocker.patch``: 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 `_ * `MagicMock `_ * `PropertyMock `_ * `ANY `_ * `DEFAULT `_ *(Version 1.4)* * `call `_ *(Version 1.1)* * `sentinel `_ *(Version 1.2)* * `mock_open `_ Spy --- The spy acts exactly like the original method in all cases, except it allows use of `mock` features with it, like retrieving call count. It also works for class and static methods. .. code-block:: python def test_spy(mocker): class Foo(object): def bar(self): return 42 foo = Foo() mocker.spy(foo, 'bar') assert foo.bar() == 42 assert foo.bar.call_count == 1 Stub ---- The stub is a mock object that accepts any arguments and is useful to test callbacks, for instance. May be passed a name to be used by the constructed stub object in its repr (useful for debugging). .. code-block:: python def test_stub(mocker): def foo(on_something): on_something('foo', 'bar') stub = mocker.stub(name='on_something_stub') foo(stub) stub.assert_called_once_with('foo', 'bar') Improved reporting of mock call assertion errors ------------------------------------------------ This plugin monkeypatches the mock library to improve pytest output for failures of mock call assertions like ``Mock.assert_called_with()`` by hiding internal traceback entries from the ``mock`` module. It also adds introspection information on differing call arguments when calling the helper methods. This features catches `AssertionError` raised in the method, and uses py.test's own `advanced assertions`_ to return a better diff:: mocker = def test(mocker): m = mocker.Mock() m('fo') > m.assert_called_once_with('', bar=4) E AssertionError: Expected call: mock('', bar=4) E Actual call: mock('fo') E E pytest introspection follows: E E Args: E assert ('fo',) == ('',) E At index 0 diff: 'fo' != '' E Use -v to get the full diff E Kwargs: E assert {} == {'bar': 4} E Right contains more items: E {'bar': 4} E Use -v to get the full diff test_foo.py:6: AssertionError ========================== 1 failed in 0.03 seconds =========================== This is useful when asserting mock calls with many/nested arguments and trying to quickly see the difference. This feature is probably safe, but if you encounter any problems it can be disabled in your ``pytest.ini`` file: .. code-block:: ini [pytest] mock_traceback_monkeypatch = false Note that this feature is automatically disabled with the ``--tb=native`` option. The underlying mechanism used to suppress traceback entries from ``mock`` module does not work with that option anyway plus it generates confusing messages on Python 3.5 due to exception chaining .. _advanced assertions: http://pytest.org/latest/assert.html Use standalone "mock" package ----------------------------- *New in version 1.4.0.* Python 3 users might want to use a newest version of the ``mock`` package as published on PyPI than the one that comes with the Python distribution. .. code-block:: ini [pytest] mock_use_standalone_module = true This will force the plugin to import ``mock`` instead of the ``unittest.mock`` module bundled with Python 3.4+. Note that this option is only used in Python 3+, as Python 2 users only have the option to use the ``mock`` package from PyPI anyway. Requirements ============ * Python 2.7, Python 3.4+ * pytest * mock (for Python 2) Install ======= Install using `pip `_: .. code-block:: console $ pip install pytest-mock Changelog ========= Please consult the `changelog page`_. .. _changelog page: https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst Why bother with a plugin? ========================= There are a number of different ``patch`` usages in the standard ``mock`` API, but IMHO they don't scale very well when you have more than one or two patches to apply. It may lead to an excessive nesting of ``with`` statements, breaking the flow of the test: .. code-block:: python import mock def test_unix_fs(): with mock.patch('os.remove'): UnixFS.rm('file') os.remove.assert_called_once_with('file') with mock.patch('os.listdir'): assert UnixFS.ls('dir') == expected # ... with mock.patch('shutil.copy'): UnixFS.cp('src', 'dst') # ... One can use ``patch`` as a decorator to improve the flow of the test: .. code-block:: python @mock.patch('os.remove') @mock.patch('os.listdir') @mock.patch('shutil.copy') def test_unix_fs(mocked_copy, mocked_listdir, mocked_remove): UnixFS.rm('file') os.remove.assert_called_once_with('file') assert UnixFS.ls('dir') == expected # ... UnixFS.cp('src', 'dst') # ... But this poses a few disadvantages: - test functions must receive the mock objects as parameter, even if you don't plan to access them directly; also, order depends on the order of the decorated ``patch`` functions; - receiving the mocks as parameters doesn't mix nicely with pytest's approach of naming fixtures as parameters, or ``pytest.mark.parametrize``; - you can't easily undo the mocking during the test execution; **Note about usage as context manager** Although mocker's API is intentionally the same as ``mock.patch``'s, its use as context manager and function decorator is **not** supported through the fixture. The purpose of this plugin is to make the use of context managers and function decorators for mocking unnecessary. Indeed, trying to use the functionality in ``mocker`` in this manner can lead to non-intuitive errors: .. code-block:: python def test_context_manager(mocker): a = A() with mocker.patch.object(a, 'doIt', return_value=True, autospec=True): assert a.doIt() == True .. code-block:: console ================================== FAILURES =================================== ____________________________ test_context_manager _____________________________ in test_context_manager with mocker.patch.object(a, 'doIt', return_value=True, autospec=True): E AttributeError: __exit__ You can however use ``mocker.mock_module`` to access the underlying ``mock`` module, e.g. to return a context manager in a fixture that mocks something temporarily: .. code-block:: python @pytest.fixture def fixture_cm(mocker): @contextlib.contextmanager def my_cm(): def mocked(): pass with mocker.mock_module.patch.object(SomeClass, 'method', mocked): yield return my_cm License ======= Distributed under the terms of the `MIT`_ license. .. _MIT: https://github.com/pytest-dev/pytest-mock/blob/master/LICENSE pytest-mock-1.7.1/setup.py0000664000372000037200000000257513245755624016401 0ustar travistravis00000000000000from setuptools import setup from io import open setup( name='pytest-mock', entry_points={ 'pytest11': ['pytest_mock = pytest_mock'], }, py_modules=['pytest_mock', '_pytest_mock_version'], platforms='any', python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', install_requires=[ 'pytest>=2.7', 'mock;python_version<"3.0"', ], 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', encoding='utf-8').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', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Topic :: Software Development :: Testing', ] ) pytest-mock-1.7.1/pytest_mock.py0000664000372000037200000002370513245755624017600 0ustar travistravis00000000000000from __future__ import unicode_literals import inspect import sys import pytest from _pytest_mock_version import version __version__ = version # pseudo-six; if this starts to require more than this, depend on six already if sys.version_info[0] == 2: # pragma: no cover text_type = unicode # noqa else: text_type = str def _get_mock_module(config): """ Import and return the actual "mock" module. By default this is "mock" for Python 2 and "unittest.mock" for Python 3, but the user can force to always use "mock" on Python 3 using the mock_use_standalone_module ini option. """ if not hasattr(_get_mock_module, '_module'): use_standalone_module = parse_ini_boolean(config.getini('mock_use_standalone_module')) if sys.version_info[0] == 2 or use_standalone_module: import mock _get_mock_module._module = mock else: import unittest.mock _get_mock_module._module = unittest.mock return _get_mock_module._module 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. """ def __init__(self, config): self._patches = [] # list of mock._patch objects self._mocks = [] # list of MagicMock objects self.mock_module = mock_module = _get_mock_module(config) self.patch = self._Patcher(self._patches, self._mocks, mock_module) # aliases for convenience self.Mock = mock_module.Mock self.MagicMock = mock_module.MagicMock self.PropertyMock = mock_module.PropertyMock self.call = mock_module.call self.ANY = mock_module.ANY self.DEFAULT = mock_module.DEFAULT 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 self.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, mock_module): self._patches = patches self._mocks = mocks self.mock_module = mock_module 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) if hasattr(mocked, 'reset_mock'): self._mocks.append(mocked) return mocked def object(self, *args, **kwargs): """API to mock.patch.object""" return self._start_patch(self.mock_module.patch.object, *args, **kwargs) def multiple(self, *args, **kwargs): """API to mock.patch.multiple""" return self._start_patch(self.mock_module.patch.multiple, *args, **kwargs) def dict(self, *args, **kwargs): """API to mock.patch.dict""" return self._start_patch(self.mock_module.patch.dict, *args, **kwargs) def __call__(self, *args, **kwargs): """API to mock.patch""" return self._start_patch(self.mock_module.patch, *args, **kwargs) @pytest.yield_fixture def mocker(pytestconfig): """ 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(pytestconfig) 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) return except AssertionError as e: if getattr(e, '_mock_introspection_applied', 0): msg = text_type(e) else: __mock_self = args[0] msg = text_type(e) if __mock_self.call_args is not None: actual_args, actual_kwargs = __mock_self.call_args msg += '\n\npytest introspection follows:\n' try: assert actual_args == args[1:] except AssertionError as e: msg += '\nArgs:\n' + text_type(e) try: assert actual_kwargs == kwargs except AssertionError as e: msg += '\nKwargs:\n' + text_type(e) e = AssertionError(msg) e._mock_introspection_applied = True raise e 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 mock_module = _get_mock_module(config) 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) parser.addini('mock_use_standalone_module', 'Use standalone "mock" (from PyPI) instead of builtin "unittest.mock" ' 'on Python 3', default=False) 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) pytest-mock-1.7.1/LICENSE0000664000372000037200000000206213245755624015663 0ustar travistravis00000000000000MIT 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.pytest-mock-1.7.1/_pytest_mock_version.py0000664000372000037200000000016413245755645021501 0ustar travistravis00000000000000# coding: utf-8 # file generated by setuptools_scm # don't change, don't track in version control version = '1.7.1' pytest-mock-1.7.1/pytest_mock.egg-info/0000775000372000037200000000000013245755645020714 5ustar travistravis00000000000000pytest-mock-1.7.1/pytest_mock.egg-info/top_level.txt0000664000372000037200000000004113245755645023441 0ustar travistravis00000000000000_pytest_mock_version pytest_mock pytest-mock-1.7.1/pytest_mock.egg-info/requires.txt0000664000372000037200000000005413245755645023313 0ustar travistravis00000000000000pytest>=2.7 [:python_version < "3.0"] mock pytest-mock-1.7.1/pytest_mock.egg-info/PKG-INFO0000664000372000037200000003342013245755645022013 0ustar travistravis00000000000000Metadata-Version: 1.2 Name: pytest-mock Version: 1.7.1 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-Content-Type: UNKNOWN Description: =========== pytest-mock =========== This plugin installs a ``mocker`` fixture which is a thin-wrapper around the patching API provided by the `mock package `_, but with the benefit of not having to worry about undoing patches at the end of a test: .. code-block:: python import os class UnixFS: @staticmethod def rm(filename): os.remove(filename) def test_unix_fs(mocker): mocker.patch('os.remove') UnixFS.rm('file') os.remove.assert_called_once_with('file') .. Using PNG badges because PyPI doesn't support SVG |python| |version| |anaconda| |ci| |appveyor| |coverage| .. |version| image:: http://img.shields.io/pypi/v/pytest-mock.svg :target: https://pypi.python.org/pypi/pytest-mock .. |anaconda| image:: https://anaconda.org/conda-forge/pytest-mock/badges/version.svg :target: https://anaconda.org/conda-forge/pytest-mock .. |ci| image:: http://img.shields.io/travis/pytest-dev/pytest-mock.svg :target: https://travis-ci.org/pytest-dev/pytest-mock .. |appveyor| image:: https://ci.appveyor.com/api/projects/status/pid1t7iuwhkm9eh6/branch/master?svg=true :target: https://ci.appveyor.com/project/pytestbot/pytest-mock .. |coverage| image:: http://img.shields.io/coveralls/pytest-dev/pytest-mock.svg :target: https://coveralls.io/r/pytest-dev/pytest-mock .. |python| image:: https://img.shields.io/pypi/pyversions/pytest-mock.svg :target: https://pypi.python.org/pypi/pytest-mock/ .. image:: http://www.opensourcecitizen.org/badge?url=github.com/pytest-dev/pytest-mock :target: http://www.opensourcecitizen.org/project?url=github.com/pytest-dev/pytest-mock If you found this library useful, donate some CPU cycles to its development efforts by clicking above. Thank you! 😇 Usage ===== The ``mocker`` fixture has the same API as `mock.patch `_, supporting the same arguments: .. code-block:: python def test_foo(mocker): # all valid calls mocker.patch('os.remove') mocker.patch.object(os, 'listdir', autospec=True) mocked_isfile = mocker.patch('os.path.isfile') The supported methods are: * ``mocker.patch``: 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 `_ * `MagicMock `_ * `PropertyMock `_ * `ANY `_ * `DEFAULT `_ *(Version 1.4)* * `call `_ *(Version 1.1)* * `sentinel `_ *(Version 1.2)* * `mock_open `_ Spy --- The spy acts exactly like the original method in all cases, except it allows use of `mock` features with it, like retrieving call count. It also works for class and static methods. .. code-block:: python def test_spy(mocker): class Foo(object): def bar(self): return 42 foo = Foo() mocker.spy(foo, 'bar') assert foo.bar() == 42 assert foo.bar.call_count == 1 Stub ---- The stub is a mock object that accepts any arguments and is useful to test callbacks, for instance. May be passed a name to be used by the constructed stub object in its repr (useful for debugging). .. code-block:: python def test_stub(mocker): def foo(on_something): on_something('foo', 'bar') stub = mocker.stub(name='on_something_stub') foo(stub) stub.assert_called_once_with('foo', 'bar') Improved reporting of mock call assertion errors ------------------------------------------------ This plugin monkeypatches the mock library to improve pytest output for failures of mock call assertions like ``Mock.assert_called_with()`` by hiding internal traceback entries from the ``mock`` module. It also adds introspection information on differing call arguments when calling the helper methods. This features catches `AssertionError` raised in the method, and uses py.test's own `advanced assertions`_ to return a better diff:: mocker = def test(mocker): m = mocker.Mock() m('fo') > m.assert_called_once_with('', bar=4) E AssertionError: Expected call: mock('', bar=4) E Actual call: mock('fo') E E pytest introspection follows: E E Args: E assert ('fo',) == ('',) E At index 0 diff: 'fo' != '' E Use -v to get the full diff E Kwargs: E assert {} == {'bar': 4} E Right contains more items: E {'bar': 4} E Use -v to get the full diff test_foo.py:6: AssertionError ========================== 1 failed in 0.03 seconds =========================== This is useful when asserting mock calls with many/nested arguments and trying to quickly see the difference. This feature is probably safe, but if you encounter any problems it can be disabled in your ``pytest.ini`` file: .. code-block:: ini [pytest] mock_traceback_monkeypatch = false Note that this feature is automatically disabled with the ``--tb=native`` option. The underlying mechanism used to suppress traceback entries from ``mock`` module does not work with that option anyway plus it generates confusing messages on Python 3.5 due to exception chaining .. _advanced assertions: http://pytest.org/latest/assert.html Use standalone "mock" package ----------------------------- *New in version 1.4.0.* Python 3 users might want to use a newest version of the ``mock`` package as published on PyPI than the one that comes with the Python distribution. .. code-block:: ini [pytest] mock_use_standalone_module = true This will force the plugin to import ``mock`` instead of the ``unittest.mock`` module bundled with Python 3.4+. Note that this option is only used in Python 3+, as Python 2 users only have the option to use the ``mock`` package from PyPI anyway. Requirements ============ * Python 2.7, Python 3.4+ * pytest * mock (for Python 2) Install ======= Install using `pip `_: .. code-block:: console $ pip install pytest-mock Changelog ========= Please consult the `changelog page`_. .. _changelog page: https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst Why bother with a plugin? ========================= There are a number of different ``patch`` usages in the standard ``mock`` API, but IMHO they don't scale very well when you have more than one or two patches to apply. It may lead to an excessive nesting of ``with`` statements, breaking the flow of the test: .. code-block:: python import mock def test_unix_fs(): with mock.patch('os.remove'): UnixFS.rm('file') os.remove.assert_called_once_with('file') with mock.patch('os.listdir'): assert UnixFS.ls('dir') == expected # ... with mock.patch('shutil.copy'): UnixFS.cp('src', 'dst') # ... One can use ``patch`` as a decorator to improve the flow of the test: .. code-block:: python @mock.patch('os.remove') @mock.patch('os.listdir') @mock.patch('shutil.copy') def test_unix_fs(mocked_copy, mocked_listdir, mocked_remove): UnixFS.rm('file') os.remove.assert_called_once_with('file') assert UnixFS.ls('dir') == expected # ... UnixFS.cp('src', 'dst') # ... But this poses a few disadvantages: - test functions must receive the mock objects as parameter, even if you don't plan to access them directly; also, order depends on the order of the decorated ``patch`` functions; - receiving the mocks as parameters doesn't mix nicely with pytest's approach of naming fixtures as parameters, or ``pytest.mark.parametrize``; - you can't easily undo the mocking during the test execution; **Note about usage as context manager** Although mocker's API is intentionally the same as ``mock.patch``'s, its use as context manager and function decorator is **not** supported through the fixture. The purpose of this plugin is to make the use of context managers and function decorators for mocking unnecessary. Indeed, trying to use the functionality in ``mocker`` in this manner can lead to non-intuitive errors: .. code-block:: python def test_context_manager(mocker): a = A() with mocker.patch.object(a, 'doIt', return_value=True, autospec=True): assert a.doIt() == True .. code-block:: console ================================== FAILURES =================================== ____________________________ test_context_manager _____________________________ in test_context_manager with mocker.patch.object(a, 'doIt', return_value=True, autospec=True): E AttributeError: __exit__ You can however use ``mocker.mock_module`` to access the underlying ``mock`` module, e.g. to return a context manager in a fixture that mocks something temporarily: .. code-block:: python @pytest.fixture def fixture_cm(mocker): @contextlib.contextmanager def my_cm(): def mocked(): pass with mocker.mock_module.patch.object(SomeClass, 'method', mocked): yield return my_cm 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 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Topic :: Software Development :: Testing Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* pytest-mock-1.7.1/pytest_mock.egg-info/dependency_links.txt0000664000372000037200000000000113245755645024762 0ustar travistravis00000000000000 pytest-mock-1.7.1/pytest_mock.egg-info/entry_points.txt0000664000372000037200000000004613245755645024212 0ustar travistravis00000000000000[pytest11] pytest_mock = pytest_mock pytest-mock-1.7.1/pytest_mock.egg-info/SOURCES.txt0000664000372000037200000000055613245755645022606 0ustar travistravis00000000000000.gitignore .travis.yml CHANGELOG.rst LICENSE 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 pytest_mock.egg-info/entry_points.txt pytest_mock.egg-info/requires.txt pytest_mock.egg-info/top_level.txtpytest-mock-1.7.1/appveyor.yml0000664000372000037200000000017713245755624017253 0ustar travistravis00000000000000install: - C:\Python36\python -m pip install tox build: false # Not a C# project test_script: - C:\Python36\scripts\tox pytest-mock-1.7.1/tox.ini0000664000372000037200000000121413245755624016167 0ustar travistravis00000000000000[tox] envlist = py{27,34,35,36}-pytest{30,31,32,33,34},linting,norewrite [testenv] passenv = USER USERNAME deps = coverage pytest30: pytest~=3.0 pytest31: pytest~=3.1 pytest32: pytest~=3.2 pytest33: pytest~=3.3 pytest34: pytest~=3.4 commands = coverage run --append --source=pytest_mock.py -m pytest test_pytest_mock.py [testenv:norewrite] commands = pytest test_pytest_mock.py --assert=plain -ra [testenv:linting] 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 pytest-mock-1.7.1/.gitignore0000664000372000037200000000115213245755624016645 0ustar travistravis00000000000000# 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 .pytest_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 # IDE .idea pytest-mock-1.7.1/.travis.yml0000664000372000037200000000144113245755624016767 0ustar travistravis00000000000000language: python python: - "2.7" - "3.4" - "3.5" - "3.6" install: - pip install tox-travis coveralls script: - tox after_success: - coveralls jobs: include: - python: '3.6' env: TOXENV=linting - python: '3.6' env: TOXENV=norewrite - stage: deploy python: '3.6' install: pip install -U setuptools setuptools_scm script: skip deploy: provider: pypi user: nicoddemus skip_upload_docs: true distributions: sdist bdist_wheel password: secure: OEWrbk09CZRrwFE6sBpRqQHu45zRu1S0Ly1ZeprkFCKxMd9tZOnrYM5qxCDQXxFHIvuyajuJ+qWTOgxUvurQMNsD6DbvJKTJ0R8upH1b1Q95KK8xiJFedhqBEUga5GrInK59oo0Sgblse2jtH5NnHXRUClSdT+iHdLY5sljCTRg= on: tags: true repo: pytest-dev/pytest-mock pytest-mock-1.7.1/setup.cfg0000664000372000037200000000010313245755645016474 0ustar travistravis00000000000000[bdist_wheel] universal = 1 [egg_info] tag_build = tag_date = 0 pytest-mock-1.7.1/CHANGELOG.rst0000664000372000037200000002362413245755624016706 0ustar travistravis000000000000001.7.1 ----- * Fix ``setup.py`` to correctly read the ``README.rst``. Thanks `@ghisvail`_ for the fix (`#107`_). .. _#107: https://github.com/pytest-dev/pytest-mock/issues/107 1.7.0 ----- **Incompatible change** * ``pytest-mock`` no longer supports Python 2.6 and Python 3.3, following the lead of ``pytest`` and other projects in the community. Thanks `@hugovk`_ for the PR (`#96`_). **Packaging** * Fix ``mock`` requirement in Python 2. Thanks `@ghisvail`_ for the report (`#101`_). **Internal** * Some tests in ``pytest-mock``'s suite are skipped if assertion rewriting is disabled (`#102`_). .. _@ghisvail: https://github.com/ghisvail .. _@hugovk: https://github.com/hugovk .. _#96: https://github.com/pytest-dev/pytest-mock/pull/96 .. _#101: https://github.com/pytest-dev/pytest-mock/issues/101 .. _#102: https://github.com/pytest-dev/pytest-mock/issues/102 1.6.3 ----- * Fix ``UnicodeDecodeError`` during assert introspection in ``assert_called_with`` in Python 2. Thanks `@AndreasHogstrom`_ for the report (`#91`_). .. _@AndreasHogstrom: https://github.com/AndreasHogstrom .. _#91: https://github.com/pytest-dev/pytest-mock/issues/91 1.6.2 ----- * Provide source package in ``tar.gz`` format and remove obsolete ``MANIFEST.in``. 1.6.1 ----- * Fix ``mocker.resetall()`` by ignoring mocker objects which don't have a ``resetall`` method, like for example ``patch.dict``. Thanks `@jdavisp3`_ for the PR (`#88`_). .. _@jdavisp3: https://github.com/jdavisp3 .. _#88: https://github.com/pytest-dev/pytest-mock/pull/88 1.6.0 ----- * The original assertions raised by the various ``Mock.assert_*`` methods now appear in the failure message, in addition to the message obtained from pytest introspection. Thanks `@quodlibetor`_ for the initial patch (`#79`_). .. _@quodlibetor: https://github.com/quodlibetor .. _#79: https://github.com/pytest-dev/pytest-mock/pull/79 1.5.0 ----- * New ``mocker.mock_module`` variable points to the underlying mock module being used (``unittest.mock`` or ``mock``). Thanks `@blueyed`_ for the request (`#71`_). .. _#71: https://github.com/pytest-dev/pytest-mock/pull/71 1.4.0 ----- * New configuration variable, ``mock_use_standalone_module`` (defaults to ``False``). This forces the plugin to import ``mock`` instead of ``unittest.mock`` on Python 3. This is useful to import a newer version than the one available in the Python distribution. * Previously the plugin would first try to import ``mock`` and fallback to ``unittest.mock`` in case of an ``ImportError``, but this behavior has been removed because it could hide hard to debug import errors (`#68`_). * Now ``mock`` (Python 2) and ``unittest.mock`` (Python 3) are lazy-loaded to make it possible to implement the new ``mock_use_standlone_module`` configuration option. As a consequence of this the undocumented ``pytest_mock.mock_module`` variable, which pointed to the actual mock module being used by the plugin, has been removed. * `DEFAULT `_ is now available from the ``mocker`` fixture. .. _#68: https://github.com/pytest-dev/pytest-mock/issues/68 1.3.0 ----- * 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 `_ 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 `_ 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 pytest-mock-1.7.1/PKG-INFO0000664000372000037200000003342013245755645015760 0ustar travistravis00000000000000Metadata-Version: 1.2 Name: pytest-mock Version: 1.7.1 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-Content-Type: UNKNOWN Description: =========== pytest-mock =========== This plugin installs a ``mocker`` fixture which is a thin-wrapper around the patching API provided by the `mock package `_, but with the benefit of not having to worry about undoing patches at the end of a test: .. code-block:: python import os class UnixFS: @staticmethod def rm(filename): os.remove(filename) def test_unix_fs(mocker): mocker.patch('os.remove') UnixFS.rm('file') os.remove.assert_called_once_with('file') .. Using PNG badges because PyPI doesn't support SVG |python| |version| |anaconda| |ci| |appveyor| |coverage| .. |version| image:: http://img.shields.io/pypi/v/pytest-mock.svg :target: https://pypi.python.org/pypi/pytest-mock .. |anaconda| image:: https://anaconda.org/conda-forge/pytest-mock/badges/version.svg :target: https://anaconda.org/conda-forge/pytest-mock .. |ci| image:: http://img.shields.io/travis/pytest-dev/pytest-mock.svg :target: https://travis-ci.org/pytest-dev/pytest-mock .. |appveyor| image:: https://ci.appveyor.com/api/projects/status/pid1t7iuwhkm9eh6/branch/master?svg=true :target: https://ci.appveyor.com/project/pytestbot/pytest-mock .. |coverage| image:: http://img.shields.io/coveralls/pytest-dev/pytest-mock.svg :target: https://coveralls.io/r/pytest-dev/pytest-mock .. |python| image:: https://img.shields.io/pypi/pyversions/pytest-mock.svg :target: https://pypi.python.org/pypi/pytest-mock/ .. image:: http://www.opensourcecitizen.org/badge?url=github.com/pytest-dev/pytest-mock :target: http://www.opensourcecitizen.org/project?url=github.com/pytest-dev/pytest-mock If you found this library useful, donate some CPU cycles to its development efforts by clicking above. Thank you! 😇 Usage ===== The ``mocker`` fixture has the same API as `mock.patch `_, supporting the same arguments: .. code-block:: python def test_foo(mocker): # all valid calls mocker.patch('os.remove') mocker.patch.object(os, 'listdir', autospec=True) mocked_isfile = mocker.patch('os.path.isfile') The supported methods are: * ``mocker.patch``: 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 `_ * `MagicMock `_ * `PropertyMock `_ * `ANY `_ * `DEFAULT `_ *(Version 1.4)* * `call `_ *(Version 1.1)* * `sentinel `_ *(Version 1.2)* * `mock_open `_ Spy --- The spy acts exactly like the original method in all cases, except it allows use of `mock` features with it, like retrieving call count. It also works for class and static methods. .. code-block:: python def test_spy(mocker): class Foo(object): def bar(self): return 42 foo = Foo() mocker.spy(foo, 'bar') assert foo.bar() == 42 assert foo.bar.call_count == 1 Stub ---- The stub is a mock object that accepts any arguments and is useful to test callbacks, for instance. May be passed a name to be used by the constructed stub object in its repr (useful for debugging). .. code-block:: python def test_stub(mocker): def foo(on_something): on_something('foo', 'bar') stub = mocker.stub(name='on_something_stub') foo(stub) stub.assert_called_once_with('foo', 'bar') Improved reporting of mock call assertion errors ------------------------------------------------ This plugin monkeypatches the mock library to improve pytest output for failures of mock call assertions like ``Mock.assert_called_with()`` by hiding internal traceback entries from the ``mock`` module. It also adds introspection information on differing call arguments when calling the helper methods. This features catches `AssertionError` raised in the method, and uses py.test's own `advanced assertions`_ to return a better diff:: mocker = def test(mocker): m = mocker.Mock() m('fo') > m.assert_called_once_with('', bar=4) E AssertionError: Expected call: mock('', bar=4) E Actual call: mock('fo') E E pytest introspection follows: E E Args: E assert ('fo',) == ('',) E At index 0 diff: 'fo' != '' E Use -v to get the full diff E Kwargs: E assert {} == {'bar': 4} E Right contains more items: E {'bar': 4} E Use -v to get the full diff test_foo.py:6: AssertionError ========================== 1 failed in 0.03 seconds =========================== This is useful when asserting mock calls with many/nested arguments and trying to quickly see the difference. This feature is probably safe, but if you encounter any problems it can be disabled in your ``pytest.ini`` file: .. code-block:: ini [pytest] mock_traceback_monkeypatch = false Note that this feature is automatically disabled with the ``--tb=native`` option. The underlying mechanism used to suppress traceback entries from ``mock`` module does not work with that option anyway plus it generates confusing messages on Python 3.5 due to exception chaining .. _advanced assertions: http://pytest.org/latest/assert.html Use standalone "mock" package ----------------------------- *New in version 1.4.0.* Python 3 users might want to use a newest version of the ``mock`` package as published on PyPI than the one that comes with the Python distribution. .. code-block:: ini [pytest] mock_use_standalone_module = true This will force the plugin to import ``mock`` instead of the ``unittest.mock`` module bundled with Python 3.4+. Note that this option is only used in Python 3+, as Python 2 users only have the option to use the ``mock`` package from PyPI anyway. Requirements ============ * Python 2.7, Python 3.4+ * pytest * mock (for Python 2) Install ======= Install using `pip `_: .. code-block:: console $ pip install pytest-mock Changelog ========= Please consult the `changelog page`_. .. _changelog page: https://github.com/pytest-dev/pytest-mock/blob/master/CHANGELOG.rst Why bother with a plugin? ========================= There are a number of different ``patch`` usages in the standard ``mock`` API, but IMHO they don't scale very well when you have more than one or two patches to apply. It may lead to an excessive nesting of ``with`` statements, breaking the flow of the test: .. code-block:: python import mock def test_unix_fs(): with mock.patch('os.remove'): UnixFS.rm('file') os.remove.assert_called_once_with('file') with mock.patch('os.listdir'): assert UnixFS.ls('dir') == expected # ... with mock.patch('shutil.copy'): UnixFS.cp('src', 'dst') # ... One can use ``patch`` as a decorator to improve the flow of the test: .. code-block:: python @mock.patch('os.remove') @mock.patch('os.listdir') @mock.patch('shutil.copy') def test_unix_fs(mocked_copy, mocked_listdir, mocked_remove): UnixFS.rm('file') os.remove.assert_called_once_with('file') assert UnixFS.ls('dir') == expected # ... UnixFS.cp('src', 'dst') # ... But this poses a few disadvantages: - test functions must receive the mock objects as parameter, even if you don't plan to access them directly; also, order depends on the order of the decorated ``patch`` functions; - receiving the mocks as parameters doesn't mix nicely with pytest's approach of naming fixtures as parameters, or ``pytest.mark.parametrize``; - you can't easily undo the mocking during the test execution; **Note about usage as context manager** Although mocker's API is intentionally the same as ``mock.patch``'s, its use as context manager and function decorator is **not** supported through the fixture. The purpose of this plugin is to make the use of context managers and function decorators for mocking unnecessary. Indeed, trying to use the functionality in ``mocker`` in this manner can lead to non-intuitive errors: .. code-block:: python def test_context_manager(mocker): a = A() with mocker.patch.object(a, 'doIt', return_value=True, autospec=True): assert a.doIt() == True .. code-block:: console ================================== FAILURES =================================== ____________________________ test_context_manager _____________________________ in test_context_manager with mocker.patch.object(a, 'doIt', return_value=True, autospec=True): E AttributeError: __exit__ You can however use ``mocker.mock_module`` to access the underlying ``mock`` module, e.g. to return a context manager in a fixture that mocks something temporarily: .. code-block:: python @pytest.fixture def fixture_cm(mocker): @contextlib.contextmanager def my_cm(): def mocked(): pass with mocker.mock_module.patch.object(SomeClass, 'method', mocked): yield return my_cm 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 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Topic :: Software Development :: Testing Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* pytest-mock-1.7.1/test_pytest_mock.py0000664000372000037200000003663713245755624020647 0ustar travistravis00000000000000import 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') @pytest.fixture def needs_assert_rewrite(pytestconfig): """ Fixture which skips requesting test if assertion rewrite is disabled (#102) Making this a fixture to avoid acessing pytest's config in the global context. """ option = pytestconfig.getoption('assertmode') if option != 'rewrite': pytest.skip('this test needs assertion rewrite to work but current option ' 'is "{}"'.format(option)) 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): r = mocker.patch.multiple('os', remove=mocker.DEFAULT, listdir=mocker.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) mocker.resetall() mocker.stopall() 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_patch_dict_resetall(mocker): """ We can call resetall after patching a dict. :param mock: """ x = {'original': 1} mocker.patch.dict(x, values=[('new', 10)], clear=True) assert x == {'new': 10} mocker.resetall() assert x == {'new': 10} 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, pytestconfig): from pytest_mock import _get_mock_module, MockFixture mock_module = _get_mock_module(pytestconfig) mocker = MockFixture(pytestconfig) 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): 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 = [mocker.call(foo, arg=10), mocker.call(other, arg=10)] assert spy.call_args_list == calls @skip_pypy def test_instance_method_by_subclass_spy(mocker): 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 = [mocker.call(foo, arg=10), mocker.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] == (3, 4), reason="assert_not_called not available in Python 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") @pytest.mark.usefixtures('needs_assert_rewrite') 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) @pytest.mark.usefixtures('needs_assert_rewrite') 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): stub = mocker.stub() stub("foo") stub.assert_has_calls([mocker.call("foo")]) with assert_traceback(): stub.assert_has_calls([mocker.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 """) result = runpytest_subprocess(testdir) 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') """) result = runpytest_subprocess(testdir, '--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) @pytest.mark.skipif(sys.version_info[0] < 3, reason='Py3 only') def test_standalone_mock(testdir): """Check that the "mock_use_standalone" is being used. """ testdir.makepyfile(""" def test_foo(mocker): pass """) testdir.makeini(""" [pytest] mock_use_standalone_module = true """) result = runpytest_subprocess(testdir) assert result.ret == 3 result.stderr.fnmatch_lines([ "*No module named 'mock'*", ]) def runpytest_subprocess(testdir, *args): """Testdir.runpytest_subprocess only available in pytest-2.8+""" if hasattr(testdir, 'runpytest_subprocess'): return testdir.runpytest_subprocess(*args) else: # pytest 2.7.X return testdir.runpytest(*args) @pytest.mark.usefixtures('needs_assert_rewrite') def test_detailed_introspection(testdir): """Check that the "mock_use_standalone" is being used. """ testdir.makepyfile(""" def test(mocker): m = mocker.Mock() m('fo') m.assert_called_once_with('', bar=4) """) result = testdir.runpytest('-s') result.stdout.fnmatch_lines([ "*AssertionError: Expected call: mock('', bar=4)*", "*Actual call: mock('fo')*", "*pytest introspection follows:*", '*Args:', "*assert ('fo',) == ('',)", "*At index 0 diff: 'fo' != ''*", "*Use -v to get the full diff*", "*Kwargs:*", "*assert {} == {'bar': 4}*", "*Right contains more items:*", "*{'bar': 4}*", "*Use -v to get the full diff*", ]) def test_assert_called_with_unicode_arguments(mocker): """Test bug in assert_call_with called with non-ascii unicode string (#91)""" stub = mocker.stub() stub(b'l\xc3\xb6k'.decode('UTF-8')) with pytest.raises(AssertionError): stub.assert_called_with(u'lak')