diff options
40 files changed, 977 insertions, 2256 deletions
diff --git a/.carthorse.yml b/.carthorse.yml deleted file mode 100644 index d8f3a9a..0000000 --- a/.carthorse.yml +++ /dev/null @@ -1,9 +0,0 @@ -carthorse: - version-from: setup.py - tag-format: "{version}" - when: - - version-not-tagged - actions: - - run: "sudo pip install -e .[build]" - - run: "twine upload -u carthorse-mock -p $PYPI_PASS dist/*" - - create-tag diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 8416914..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,118 +0,0 @@ -version: 2.1 - -orbs: - python: cjw296/python-ci@2 - -jobs: - check-package: - parameters: - image: - type: string - python: - type: string - default: "python" - docker: - - image: << parameters.image >> - steps: - - python/check-package: - package: "mock" - test: - - run: - name: "Import package" - command: << parameters.python >> -c "import mock" - - -common: &common - jobs: - - python/pip-run-tests: - name: python27 - image: circleci/python:2.7 - - python/pip-run-tests: - name: python34 - image: circleci/python:3.4 - - python/pip-run-tests: - name: python35 - image: circleci/python:3.5 - - python/pip-run-tests: - name: python36 - image: circleci/python:3.6 - - python/pip-run-tests: - name: python37 - image: circleci/python:3.7 - - python/pip-run-tests: - name: pypy27 - image: pypy:2.7 - - python/pip-run-tests: - name: pypy36 - image: pypy:3.6 - - - python/coverage: - name: coverage - requires: - - python27 - - python34 - - python35 - - python36 - - python37 - - pypy27 - - pypy36 - - - python/pip-docs: - name: docs - requires: - - coverage - - - python/pip-setuptools-build-package: - name: package - requires: - - docs - filters: - branches: - only: master - - - check-package: - name: check-package-python27 - image: circleci/python:2.7 - requires: - - package - - - check-package: - name: check-package-python37 - image: circleci/python:3.7 - requires: - - package - - - check-package: - name: check-package-pypy27 - image: pypy:2.7 - python: pypy - requires: - - package - - - check-package: - name: check-package-pypy36 - image: pypy:3.6 - python: pypy3 - requires: - - package - - - python/release: - name: release - config: .carthorse.yml - requires: - - check-package-python27 - - check-package-python37 - - check-package-pypy27 - - check-package-pypy36 - -workflows: - push: - <<: *common - periodic: - <<: *common - triggers: - - schedule: - cron: "0 1 * * *" - filters: - branches: - only: master diff --git a/.coveragerc b/.coveragerc deleted file mode 100644 index 5a29219..0000000 --- a/.coveragerc +++ /dev/null @@ -1,14 +0,0 @@ -[run] -source = mock -omit = mock/tests/__main__.py - -[report] -exclude_lines = - pragma: no cover - if __name__ == .__main__.: - : pass - -[paths] -source = - mock/ - /root/project/mock/ @@ -1,4 +1,5 @@ .*\.pyc +*.rej html/ mock\.egg-info/ mock\.wpu @@ -12,6 +13,8 @@ runtox *.pyc .testrepository .*.swp +AUTHORS +ChangeLog +.eggs +README.saved README.html -.coverage -.coverage.* diff --git a/.readthedocs.yml b/.readthedocs.yml deleted file mode 100644 index 7687b8a..0000000 --- a/.readthedocs.yml +++ /dev/null @@ -1,8 +0,0 @@ -version: 2 -python: - version: 3.7 - install: - - method: pip - path: . - extra_requirements: - - docs diff --git a/.testr.conf b/.testr.conf new file mode 100644 index 0000000..8a65628 --- /dev/null +++ b/.testr.conf @@ -0,0 +1,4 @@ +[DEFAULT] +test_command=${PYTHON:-python} -m subunit.run discover . $LISTOPT $IDOPTION +test_id_option=--load-list $IDFILE +test_list_option=--list diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a13b528 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,26 @@ +sudo: false +language: python +python: + - "2.7" + - "3.3" + - "3.4" + - "3.5" + - "3.6" + - pypy + - pypy3 +matrix: + include: +# Travis nightly look to be 3.5.0a4, b3 is out and the syntax error we see +# doesn't happen in trunk. + - python: "nightly" + env: SKIP_DOCS=1 +install: + - pip install -U pip + - pip install -U wheel setuptools + - pip install -U .[docs,test] + - pip list + - python --version +script: + - unit2 + - if [ -z "$SKIP_DOCS" ]; then python setup.py build_sphinx; fi + - rst2html.py --strict README.rst README.html diff --git a/CHANGELOG.rst b/CHANGELOG.rst deleted file mode 100644 index 919648b..0000000 --- a/CHANGELOG.rst +++ /dev/null @@ -1,206 +0,0 @@ -3.0.5 ------ - -- Issue #31855: :func:`unittest.mock.mock_open` results now respects the - argument of read([size]). Patch contributed by Rémi Lapeyre. - -3.0.4 ------ - -- Include the license, readme and changelog in the source distribution. - -3.0.3 ------ - -- Fixed patching of dictionaries, when specifying the target with a - unicode on Python 2. - -3.0.2 ------ - -- Add missing ``funcsigs`` dependency on Python 2. - -3.0.1 ------ - -- Fix packaging issue where ``six`` was missed as a dependency. - -3.0.0 ------ - -- Issue #35226: Recursively check arguments when testing for equality of - :class:`unittest.mock.call` objects and add note that tracking of - parameters used to create ancestors of mocks in ``mock_calls`` is not - possible. - -- Issue #31177: Fix bug that prevented using :meth:`reset_mock - <unittest.mock.Mock.reset_mock>` on mock instances with deleted attributes - -- Issue #26704: Added test demonstrating double-patching of an instance - method. Patch by Anthony Sottile. - -- Issue #35500: Write expected and actual call parameters on separate lines - in :meth:`unittest.mock.Mock.assert_called_with` assertion errors. - Contributed by Susan Su. - -- Issue #35330: When a :class:`Mock` instance was used to wrap an object, if - `side_effect` is used in one of the mocks of it methods, don't call the - original implementation and return the result of using the side effect the - same way that it is done with return_value. - -- Issue #30541: Add new function to seal a mock and prevent the - automatically creation of child mocks. Patch by Mario Corchero. - -- Issue #35022: :class:`unittest.mock.MagicMock` now supports the - ``__fspath__`` method (from :class:`os.PathLike`). - -- Issue #33516: :class:`unittest.mock.MagicMock` now supports the - ``__round__`` magic method. - -- Issue #35512: :func:`unittest.mock.patch.dict` used as a decorator with - string target resolves the target during function call instead of during - decorator construction. Patch by Karthikeyan Singaravelan. - -- Issue #36366: Calling ``stop()`` on an unstarted or stopped - :func:`unittest.mock.patch` object will now return `None` instead of - raising :exc:`RuntimeError`, making the method idempotent. Patch - byKarthikeyan Singaravelan. - -- Issue #35357: Internal attributes' names of unittest.mock._Call and - unittest.mock.MagicProxy (name, parent & from_kall) are now prefixed with - _mock_ in order to prevent clashes with widely used object attributes. - Fixed minor typo in test function name. - -- Issue #20239: Allow repeated assignment deletion of - :class:`unittest.mock.Mock` attributes. Patch by Pablo Galindo. - -- Issue #35082: Don't return deleted attributes when calling dir on a - :class:`unittest.mock.Mock`. - -- Issue #0: Improved an error message when mock assert_has_calls fails. - -- Issue #23078: Add support for :func:`classmethod` and :func:`staticmethod` - to :func:`unittest.mock.create_autospec`. Initial patch by Felipe Ochoa. - -- Issue #21478: Calls to a child function created with - :func:`unittest.mock.create_autospec` should propagate to the parent. - Patch by Karthikeyan Singaravelan. - -- Issue #36598: Fix ``isinstance`` check for Mock objects with spec when the - code is executed under tracing. Patch by Karthikeyan Singaravelan. - -- Issue #32933: :func:`unittest.mock.mock_open` now supports iteration over - the file contents. Patch by Tony Flury. - -- Issue #21269: Add ``args`` and ``kwargs`` properties to mock call objects. - Contributed by Kumar Akshay. - -- Issue #17185: Set ``__signature__`` on mock for :mod:`inspect` to get - signature. Patch by Karthikeyan Singaravelan. - -- Issue #35047: ``unittest.mock`` now includes mock calls in exception - messages if ``assert_not_called``, ``assert_called_once``, or - ``assert_called_once_with`` fails. Patch by Petter Strandmark. - -- Issue #28380: unittest.mock Mock autospec functions now properly support - assert_called, assert_not_called, and assert_called_once. - -- Issue #28735: Fixed the comparison of mock.MagickMock with mock.ANY. - -- Issue #20804: The unittest.mock.sentinel attributes now preserve their - identity when they are copied or pickled. - -- Issue #28961: Fix unittest.mock._Call helper: don't ignore the name parameter - anymore. Patch written by Jiajun Huang. - -- Issue #26750: unittest.mock.create_autospec() now works properly for - subclasses of property() and other data descriptors. - -- Issue #21271: New keyword only parameters in reset_mock call. - -- Issue #26807: mock_open 'files' no longer error on readline at end of file. - Patch from Yolanda Robla. - -- Issue #25195: Fix a regression in mock.MagicMock. _Call is a subclass of - tuple (changeset 3603bae63c13 only works for classes) so we need to - implement __ne__ ourselves. Patch by Andrew Plummer. - -2.0.0 and earlier ------------------ - -- Issue #26323: Add Mock.assert_called() and Mock.assert_called_once() - methods to unittest.mock. Patch written by Amit Saha. - -- Issue #22138: Fix mock.patch behavior when patching descriptors. Restore - original values after patching. Patch contributed by Sean McCully. - -- Issue #24857: Comparing call_args to a long sequence now correctly returns a - boolean result instead of raising an exception. Patch by A Kaptur. - -- Issue #23004: mock_open() now reads binary data correctly when the type of - read_data is bytes. Initial patch by Aaron Hill. - -- Issue #21750: mock_open.read_data can now be read from each instance, as it - could in Python 3.3. - -- Issue #18622: unittest.mock.mock_open().reset_mock would recurse infinitely. - Patch from Nicola Palumbo and Laurent De Buyst. - -- Issue #23661: unittest.mock side_effects can now be exceptions again. This - was a regression vs Python 3.4. Patch from Ignacio Rossi - -- Issue #23310: Fix MagicMock's initializer to work with __methods__, just - like configure_mock(). Patch by Kasia Jachim. - -- Issue #23568: Add rdivmod support to MagicMock() objects. - Patch by Håkan Lövdahl. - -- Issue #23581: Add matmul support to MagicMock. Patch by Håkan Lövdahl. - -- Issue #23326: Removed __ne__ implementations. Since fixing default __ne__ - implementation in issue #21408 they are redundant. *** NOT BACKPORTED *** - -- Issue #21270: We now override tuple methods in mock.call objects so that - they can be used as normal call attributes. - -- Issue #21256: Printout of keyword args should be in deterministic order in - a mock function call. This will help to write better doctests. - -- Issue #21262: New method assert_not_called for Mock. - It raises AssertionError if the mock has been called. - -- Issue #21238: New keyword argument `unsafe` to Mock. It raises - `AttributeError` incase of an attribute startswith assert or assret. - -- Issue #21239: patch.stopall() didn't work deterministically when the same - name was patched more than once. - -- Issue #21222: Passing name keyword argument to mock.create_autospec now - works. - -- Issue #17826: setting an iterable side_effect on a mock function created by - create_autospec now works. Patch by Kushal Das. - -- Issue #17826: setting an iterable side_effect on a mock function created by - create_autospec now works. Patch by Kushal Das. - -- Issue #20968: unittest.mock.MagicMock now supports division. - Patch by Johannes Baiter. - -- Issue #20189: unittest.mock now no longer assumes that any object for - which it could get an inspect.Signature is a callable written in Python. - Fix courtesy of Michael Foord. - -- Issue #17467: add readline and readlines support to mock_open in - unittest.mock. - -- Issue #17015: When it has a spec, a Mock object now inspects its signature - when matching calls, so that arguments can be matched positionally or - by name. - -- Issue #15323: improve failure message of Mock.assert_called_once_with - -- Issue #14857: fix regression in references to PEP 3135 implicit __class__ - closure variable (Reopens issue #12370) - -- Issue #14295: Add unittest.mock diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 7f47ab6..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include LICENSE.txt -include *.rst @@ -0,0 +1,79 @@ +Library +------- + +- Issue #26323: Add Mock.assert_called() and Mock.assert_called_once() + methods to unittest.mock. Patch written by Amit Saha. + +- Issue #22138: Fix mock.patch behavior when patching descriptors. Restore + original values after patching. Patch contributed by Sean McCully. + +- Issue #24857: Comparing call_args to a long sequence now correctly returns a + boolean result instead of raising an exception. Patch by A Kaptur. + +- Issue #23004: mock_open() now reads binary data correctly when the type of + read_data is bytes. Initial patch by Aaron Hill. + +- Issue #21750: mock_open.read_data can now be read from each instance, as it + could in Python 3.3. + +- Issue #18622: unittest.mock.mock_open().reset_mock would recurse infinitely. + Patch from Nicola Palumbo and Laurent De Buyst. + +- Issue #23661: unittest.mock side_effects can now be exceptions again. This + was a regression vs Python 3.4. Patch from Ignacio Rossi + +- Issue #23310: Fix MagicMock's initializer to work with __methods__, just + like configure_mock(). Patch by Kasia Jachim. + +- Issue #23568: Add rdivmod support to MagicMock() objects. + Patch by Håkan Lövdahl. + +- Issue #23581: Add matmul support to MagicMock. Patch by Håkan Lövdahl. + +- Issue #23326: Removed __ne__ implementations. Since fixing default __ne__ + implementation in issue #21408 they are redundant. *** NOT BACKPORTED *** + +- Issue #21270: We now override tuple methods in mock.call objects so that + they can be used as normal call attributes. + +- Issue #21256: Printout of keyword args should be in deterministic order in + a mock function call. This will help to write better doctests. + +- Issue #21262: New method assert_not_called for Mock. + It raises AssertionError if the mock has been called. + +- Issue #21238: New keyword argument `unsafe` to Mock. It raises + `AttributeError` incase of an attribute startswith assert or assret. + +- Issue #21239: patch.stopall() didn't work deterministically when the same + name was patched more than once. + +- Issue #21222: Passing name keyword argument to mock.create_autospec now + works. + +- Issue #17826: setting an iterable side_effect on a mock function created by + create_autospec now works. Patch by Kushal Das. + +- Issue #17826: setting an iterable side_effect on a mock function created by + create_autospec now works. Patch by Kushal Das. + +- Issue #20968: unittest.mock.MagicMock now supports division. + Patch by Johannes Baiter. + +- Issue #20189: unittest.mock now no longer assumes that any object for + which it could get an inspect.Signature is a callable written in Python. + Fix courtesy of Michael Foord. + +- Issue #17467: add readline and readlines support to mock_open in + unittest.mock. + +- Issue #17015: When it has a spec, a Mock object now inspects its signature + when matching calls, so that arguments can be matched positionally or + by name. + +- Issue #15323: improve failure message of Mock.assert_called_once_with + +- Issue #14857: fix regression in references to PEP 3135 implicit __class__ + closure variable (Reopens issue #12370) + +- Issue #14295: Add unittest.mock @@ -7,7 +7,7 @@ mock is now part of the Python standard library, available as `unittest.mock onwards. This package contains a rolling backport of the standard library mock code -compatible with Python 2.7 and 3.4 and up. +compatible with Python 2.7 and 3.3 and up. Please see the standard library documentation for more details. @@ -17,20 +17,13 @@ Please see the standard library documentation for more details. :License: `BSD License`_ :Support: `Mailing list (testing-in-python@lists.idyll.org) <http://lists.idyll.org/listinfo/testing-in-python>`_ -:Code: `GitHub - <https://github.com/testing-cabal/mock>`_ :Issue tracker: `GitHub Issues <https://github.com/testing-cabal/mock/issues>`_ :Build status: - |CircleCI|_ |Docs|_ + .. image:: https://travis-ci.org/testing-cabal/mock.svg?branch=master + :target: https://travis-ci.org/testing-cabal/mock - .. |CircleCI| image:: https://circleci.com/gh/testing-cabal/mock/tree/master.svg?style=shield - .. _CircleCI: https://circleci.com/gh/testing-cabal/mock/tree/master - - .. |Docs| image:: https://readthedocs.org/projects/mock/badge/?version=latest - .. _Docs: http://mock.readthedocs.org/en/latest/ - -.. _Mock Homepage: http://mock.readthedocs.org/en/latest/ -.. _BSD License: https://github.com/testing-cabal/mock/blob/master/LICENSE.txt +.. _Mock Homepage: https://github.com/testing-cabal/mock +.. _BSD License: http://github.com/testing-cabal/mock/blob/master/LICENSE.txt .. _Python Docs: https://docs.python.org/dev/library/unittest.mock.html -.. _mock on PyPI: https://pypi.org/project/mock/ +.. _mock on PyPI: http://pypi.python.org/pypi/mock diff --git a/TEST_MAPPING b/TEST_MAPPING deleted file mode 100644 index 61a80b2..0000000 --- a/TEST_MAPPING +++ /dev/null @@ -1,8 +0,0 @@ -{ - "presubmit" : [ - { - "name" : "acloud_test", - "host" : true - } - ] -} diff --git a/backport.py b/backport.py deleted file mode 100644 index b2ab523..0000000 --- a/backport.py +++ /dev/null @@ -1,147 +0,0 @@ -import re -from argparse import ArgumentParser -from os.path import dirname, abspath, join -from subprocess import check_output, call - - -def git(command, repo): - return check_output('git '+command, cwd=repo, shell=True).decode() - - -def repo_state_bad(mock_repo): - status = git('status', mock_repo) - if 'You are in the middle of an am session' in status: - print(f'Mock repo at {mock_repo} needs cleanup:\n') - call('git status', shell=True) - return True - - -def cleanup_old_patches(mock_repo): - print('cleaning up old patches:') - call('rm -vf /tmp/*.mock.patch', shell=True) - call('find . -name "*.rej" -print -delete', shell=True, cwd=mock_repo) - - -def find_initial_cpython_rev(): - with open('lastsync.txt') as source: - return source.read().strip() - - -def cpython_revs_affecting_mock(cpython_repo, start): - revs = git(f'log --no-merges --format=%H {start}.. ' - f'-- Lib/unittest/mock.py Lib/unittest/test/testmock/', - repo=cpython_repo).split() - revs.reverse() - print(f'{len(revs)} patches that may need backporting') - return revs - - -def has_been_backported(mock_repo, cpython_rev): - backport_rev = git(f'log --format=%H --grep "Backports: {cpython_rev}"', - repo=mock_repo).strip() - if backport_rev: - print(f'{cpython_rev} backported in {backport_rev}') - return True - print(f'{cpython_rev} has not been backported') - - -def extract_patch_for(cpython_repo, rev): - return git(f'format-patch -1 --no-stat --keep-subject --signoff --stdout {rev}', - repo=cpython_repo) - - -def munge(rev, patch): - - sign_off = 'Signed-off-by:' - patch = patch.replace(sign_off, f'Backports: {rev}\n{sign_off}', 1) - - for pattern, sub in ( - ('(a|b)/Lib/unittest/mock.py', r'\1/mock/mock.py'), - (r'(a|b)/Lib/unittest/test/testmock/(\S+)', r'\1/mock/tests/\2'), - ('(a|b)/Misc/NEWS', r'\1/NEWS'), - ('(a|b)/NEWS.d/next/[^/]+/(.+\.rst)', r'\1/NEWS.d/\2'), - ): - patch = re.sub(pattern, sub, patch) - return patch - - -def apply_patch(mock_repo, rev, patch): - patch_path = f'/tmp/{rev}.mock.patch' - - with open(patch_path, 'w') as target: - target.write(patch) - print(f'wrote {patch_path}') - - call(f'git am -k ' - f'--include "mock/*" --include NEWS --include "NEWS.d/*" ' - f'--reject {patch_path} ', - cwd=mock_repo, shell=True) - - -def update_last_sync(mock_repo, rev): - with open(join(mock_repo, 'lastsync.txt'), 'w') as target: - target.write(rev+'\n') - print(f'update lastsync.txt to {rev}') - - -def rev_from_mock_patch(text): - match = re.search('Backports: ([a-z0-9]+)', text) - return match.group(1) - - -def skip_current(mock_repo, reason): - text = git('am --show-current-patch', repo=mock_repo) - rev = rev_from_mock_patch(text) - git('am --abort', repo=mock_repo) - print(f'skipping {rev}') - update_last_sync(mock_repo, rev) - call(f'git commit -m "Backports: {rev}, skipped: {reason}" lastsync.txt', shell=True, cwd=mock_repo) - cleanup_old_patches(mock_repo) - - -def commit_last_sync(revs, mock_repo): - print('Yay! All caught up!') - if len(revs): - git('commit -m "latest sync point" lastsync.txt', repo=mock_repo) - - -def main(): - args = parse_args() - - if args.skip_current: - return skip_current(args.mock, args.skip_reason) - - if repo_state_bad(args.mock): - return - - cleanup_old_patches(args.mock) - - initial_cpython_rev = find_initial_cpython_rev() - - revs = cpython_revs_affecting_mock(args.cpython, initial_cpython_rev) - for rev in revs: - - if has_been_backported(args.mock, rev): - update_last_sync(args.mock, rev) - continue - - patch = extract_patch_for(args.cpython, rev) - patch = munge(rev, patch) - apply_patch(args.mock, rev, patch) - break - - else: - commit_last_sync(revs, args.mock) - - -def parse_args(): - parser = ArgumentParser() - parser.add_argument('--cpython', default='../cpython') - parser.add_argument('--mock', default=abspath(dirname(__file__))) - parser.add_argument('--skip-current', action='store_true') - parser.add_argument('--skip-reason', default='it has no changes needed here.') - return parser.parse_args() - - -if __name__ == '__main__': - main() diff --git a/docs/changelog.txt b/docs/changelog.txt index 4de03af..22ec9b8 100644..120000 --- a/docs/changelog.txt +++ b/docs/changelog.txt @@ -1,4 +1 @@ -Changelog -========= - -.. include:: ../CHANGELOG.rst +../ChangeLog
\ No newline at end of file diff --git a/docs/conf.py b/docs/conf.py index d2be5a5..d32357d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -33,6 +33,7 @@ import os import sys import mock from mock import * # yeah, I know :-/ +import unittest2 import __main__ if os.getcwd() not in sys.path: @@ -71,7 +72,10 @@ copyright = u'2007-2015, Michael Foord & the mock team' # The default replacements for |version| and |release|, also used in various # other places throughout the built documents. Supplied by pbr. # -version = release = mock.mock.__version__ +# The short X.Y version. +version = mock.mock._v.brief_string() +# The full version, including alpha/beta/rc tags. +release = mock.__version__ # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: (Set from pbr) diff --git a/docs/index.txt b/docs/index.txt index 4e8bc17..6c8e37b 100644 --- a/docs/index.txt +++ b/docs/index.txt @@ -1,29 +1,58 @@ -=================================== +==================================== Mock - Mocking and Testing Library -=================================== - -.. include:: ../README.rst +==================================== + +:Version: |release| +:Date: |today| +:Homepage: `Mock Homepage`_ +:Download: `Mock on PyPI`_ +:Documentation: `Python Docs`_ +:License: `BSD License`_ +:Support: `Mailing list (testing-in-python@lists.idyll.org) + <http://lists.idyll.org/listinfo/testing-in-python>`_ +:Issue tracker: `GitHub Issues + <https://github.com/testing-cabal/mock/issues>`_ +:Last sync: cb6aab1248c4aec4dd578bea717854505a6fb55d + +.. _Mock Homepage: https://github.com/testing-cabal/mock +.. _BSD License: http://github.com/testing-cabal/mock/blob/master/LICENSE.txt +.. _Python Docs: https://docs.python.org/dev/library/unittest.mock.html .. module:: mock :synopsis: Mock object and testing library. .. index:: introduction +TOC ++++ + .. toctree:: - :hidden: + :maxdepth: 2 changelog -Python Version Compatibility -++++++++++++++++++++++++++++ +Introduction +++++++++++++ + +mock is a library for testing in Python. It allows you to replace parts of +your system under test with mock objects and make assertions about how they +have been used. -* Version 1.0.1 is the last version compatible with Python < 2.6. +mock is now part of the Python standard library, available as +``unittest.mock`` in Python 3.3 onwards. However, if you are writing code that +runs on multiple versions of Python the ``mock`` package is better, as you get +the newest features from the latest release of Python available for all +Pythons. -* Version 1.3.0 is the last version compatible with Python 3.2. +The ``mock`` package contains a rolling backport of the standard library mock +code compatible with Python 2.7 and 3.3 and up. -* Version 2.0.0 is the last version compatible with Python 2.6. +* Python 2.6 is supported by mock 2.0.0 and below. -* Version 2.0.0 is the last version offering official Jython support. +* Python 3.2 is supported by mock 1.3.0 and below - with pip no longer +supporting 3.2, we cannot test against that version anymore. + +Please see the standard library documentation for usage details. .. index:: installing .. _installing: @@ -31,13 +60,17 @@ Python Version Compatibility Installing ++++++++++ +The current version is |release|. Mock is stable and widely used. + +* `mock on PyPI <http://pypi.python.org/pypi/mock>`_ + .. index:: repository .. index:: git You can checkout the latest development version from GitHub repository with the following command: - ``git clone https://github.com/testing-cabal/mock.git`` + ``git clone https://github.com/testing-cabal/mock`` .. index:: pip @@ -46,11 +79,18 @@ You can install mock with pip: | ``pip install -U mock`` +Alternatively you can download the mock distribution from PyPI and after +unpacking run: + + ``python setup.py install`` + + .. index:: bug reports Bug Reports +++++++++++ +Mock uses `unittest2 <http://pypi.python.org/pypi/unittest2>`_ for its own Issues with the backport process, such as compatibility with a particular Python, should be reported to the `bug tracker <https://github.com/testing-cabal/mock/issues>`_. Feature requests and issues @@ -59,10 +99,21 @@ with Mock functionality should be reported to the `Python bug tracker .. index:: python changes -Changelog -+++++++++ +Python Changes +++++++++++++++ + +Python NEWS entries from cPython: + +.. include:: ../NEWS -See the :doc:`change log <changelog>`. +.. index:: older versions + +Older Versions of Python +++++++++++++++++++++++++ + +Version 1.0.1 is the last version compatible with Python < 2.6. + +Version 2.0.0 is the last version compatible with Python 2.6. .. index:: maintainer notes @@ -70,112 +121,74 @@ Maintainer Notes ++++++++++++++++ Development ------------ +=========== Checkout from git (see :ref:`installing`) and submit pull requests. Committers can just push as desired: since all semantic development takes place in cPython, the backport process is as lightweight as we can make it. -mock is CI tested using Travis-CI on Python versions 2.7, 3.4, -3.5, 3.6, pypy, pypy3. - -If you end up fixing anything backport-specific, please add an entry -to the top of ``CHANGELOG.rst`` so it shows up in the next release -notes. +mock is CI tested using Travis-CI on Python versions 2.7, 3.3, 3.4, +3.5, nightly Python 3 builds, pypy, pypy3. Jython support is desired, if +someone could contribute a patch to .travis.yml to support it that would be +excellent. Releasing ---------- +========= NB: please use semver. Bump the major component on API breaks, minor on all non-bugfix changes, patch on bugfix only changes. -1. Run ``release.py [major|minor|bugfix]`` which will roll out new - NEWS items, bump the version number and create a commit for the release. - -2. Review that commit, feel free to amend it if you want to note anything - manually in ``CHANGELOG.rst``. +1. tag -s, push --tags origin master +2. setup.py sdist bdist_wheel upload -s -3. Push to the ``master`` branch on - https://github.com/testing-cabal/mock.git and the Circle CI - automation will take care of pushing releases to PyPI and - creating a tag. Backporting rules ------------------ - -- ``isinstance`` checks in cPython to ``type`` need to check ``ClassTypes``. - Code calling ``obj.isidentifier`` needs to change to ``_isidentifier(obj)``. - -- f-strings need to be rewritten using some other string substitution. - -- ``assertRaisesRegex`` needs to be ``assertRaisesRegexp`` for Python 2. - -- If test code won't compile on a particular version of Python, move it to - a matching ``_py{version}.py`` file. If ``{version}`` isn't 3, adjust - ``conftest.py``. - -- If code such as this causes coverage checking to drop below 100%: +================= - .. code-block:: python - - def will_never_be_called(): - pass - - It should be adjusted to the following pattern, preferably upstream, - so that the ``.coveragerc`` in this repo knows to ignore it: - - .. code-block:: python - - def will_never_be_called(): pass +isinstance checks in cPython to ``type`` need to check ``ClassTypes``. +Code calling ``obj.isidentifier`` needs to change to ``_isidentifier(obj)``. Backporting process -------------------- - -1. Clone cpython and mock into the same directory, eg: - - .. code-block:: bash - - mkdir vcs - cd vcs - git clone https://github.com/python/cpython.git - git clone https://github.com/testing-cabal/mock.git - - Make sure they both on master and up to date! - -2. Create a branch in your ``mock`` clone and switch to it. - -3. Make sure you build a suitable virtualenv for Mock development - and activate it. For backporting, this should use Python 3.7+. - -4. Run ``backport.py``: - - .. code-block:: bash - - cd vcs/mock - python backport.py - - This will find the next cpython patch that needs to be applied, munge it - and attempt to apply it with ``git am``. - - If it succeeds, run the tests and/or push your branch up to a fork and - do a pull request into the master branch of the main repo to kick off - the continuous integration tests. - - If it fails, you'll have to manually work with what ``git status`` shows - to get the patch committed. - - If it turns out that there's nothing that should be applied from the failed commit, - run ``python backport.py --skip-current``, maybe with ``--skip-reason``. - - If you have to make changes, please do a ``git commit --amend`` and add notes - about what needed doing below the ``Signed-off-by`` block. - - If you have to make changes because tests fail with an applied patch, please - make those changes in a followup commit and take note of the "Backporting rules" - above. - -5. Rinse and repeat until ``backport.py`` reports no more patches need applying. - -6. If ``backport.py`` has updated ``lastsync.txt``, now would be a good time - to commit that change. +=================== + +1. Patch your git am with `my patch <https://github.com/rbtcollins/git>`_. +2. Install the applypatch-transform hook from tools/ to your .git hooks dir. +3. Configure a pre-applypatch hook to test at least all the cPython versions + we support on each patch that is applied. I use containers, and a sample + script is in tools/pre-applypatch. +4. Pull down the cPython git mirror: https://github.com/python/cpython.git +5. Export the new revisions since the ``Last sync`` at the top of this + document:: + + revs=${lastsync} + rm migrate-export + git log --pretty="format:%H " $revs.. -- Lib/unittest/mock.py \ + Lib/unittest/test/testmock/ > migrate-revs + tac migrate-revs > migrate-sorted-revs + for rev in $(< migrate-sorted-revs); do + git format-patch -1 $rev -k --stdout >> migrate-export; + done + echo NEW SYNC POINT: $(git rev-parse HEAD) + +6. Import into mock:: + + git am -k --reject $path-to-cpython/migrate-export + + This will transform the patches automatically. Currently it will error + on every NEWS change as I haven't gotten around to making those patches + automatic. Fixup any errors that occur. When the patch is ready, do a ``git + add -u`` to update the index and then ``git am --continue`` to move onto + the next patch. If the patch is inappropriate e.g. the patch removing + __ne__ which would break older pythons, then either do ``git reset --hard; + git am --skip`` to discard any partially applied changes and skip over it, + or, if it has a NEWS entry thats worth preserving, edit it down to just + that, with a note such as we have for the ``__ne__`` patch, and continue on + from there. + + The goal is that every patch work at all times. + +7. After the import is complete, update this document with the new sync point. + +8. Push to a personal branch and propose a PR to the main repo. This will make + Travis-CI test it. If it works, push to the main repo. diff --git a/extendmock.py b/extendmock.py new file mode 100644 index 0000000..0550d9f --- /dev/null +++ b/extendmock.py @@ -0,0 +1 @@ +# merged into mock.py in Mock 0.7 diff --git a/lastsync.txt b/lastsync.txt deleted file mode 100644 index 1f18392..0000000 --- a/lastsync.txt +++ /dev/null @@ -1 +0,0 @@ -11a8832c98b3db78727312154dd1d3ba76d639ec diff --git a/mock.wpr b/mock.wpr new file mode 100644 index 0000000..e1ded97 --- /dev/null +++ b/mock.wpr @@ -0,0 +1,26 @@ +#!wing +#!version=4.0 +################################################################## +# Wing IDE project file # +################################################################## +[project attributes] +proj.directory-list = [{'dirloc': loc('.'), + 'excludes': [u'latex', + u'.hg', + u'.tox', + u'dist', + u'htmlcov', + u'extendmock.py', + u'__pycache__', + u'html', + u'build', + u'mock.egg-info', + u'tests/__pycache__', + u'.hgignore', + u'.hgtags'], + 'filter': '*', + 'include_hidden': 0, + 'recursive': 1, + 'watch_for_changes': 1}] +proj.file-type = 'shared' +testing.auto-test-file-specs = ('test*.py',) diff --git a/mock/mock.py b/mock/mock.py index 2d39253..beedd69 100644 --- a/mock/mock.py +++ b/mock/mock.py @@ -2,6 +2,7 @@ # Test tools for mocking and patching. # E-mail: fuzzyman AT voidspace DOT org DOT uk # +# mock 2.0.0 # http://www.voidspace.org.uk/python/mock/ # # Copyright (c) 2007-2013, Michael Foord & the mock team @@ -50,12 +51,10 @@ __all__ = ( 'NonCallableMagicMock', 'mock_open', 'PropertyMock', - 'seal', ) from functools import partial -import io import inspect import pprint import sys @@ -63,14 +62,21 @@ try: import builtins except ImportError: import __builtin__ as builtins -from types import ModuleType, MethodType -from unittest.util import safe_repr +from types import ModuleType import six from six import wraps - -__version__ = '3.0.5' -version_info = tuple(int(p) for p in __version__.split('.')) +# GOOGLE +# We're going to avoid using pbr so we don't have to import it into AOSP just +# for the version info, instead we'll hardcode it. This will be safe since mock +# dev is essentially stable and hasn't been changed in months. +# from pbr.version import VersionInfo + +# _v = VersionInfo('mock').semantic_version() +# __version__ = _v.release_string() +# version_info = _v.version_tuple() +__version__ = '2.0.0' +version_info = (2, 0, 0, 'final', 0) import mock @@ -105,7 +111,13 @@ if six.PY2: del _next -_builtins = {name for name in dir(builtins) if not name.startswith('_')} +_builtins = set(name for name in dir(builtins) if not name.startswith('_')) + +BaseExceptions = (BaseException,) +if 'java' in sys.platform: + # jython + import java + BaseExceptions = (BaseException, java.lang.Throwable) try: _isidentifier = str.isidentifier @@ -119,6 +131,11 @@ except AttributeError: return False return regex.match(string) +self = 'im_self' +builtin = '__builtin__' +if six.PY3: + self = '__self__' + builtin = 'builtins' # NOTE: This FILTER_DIR is not used. The binding in mock.FILTER_DIR is. FILTER_DIR = True @@ -135,8 +152,8 @@ def _is_instance_mock(obj): def _is_exception(obj): return ( - isinstance(obj, BaseException) or - isinstance(obj, ClassTypes) and issubclass(obj, BaseException) + isinstance(obj, BaseExceptions) or + isinstance(obj, ClassTypes) and issubclass(obj, BaseExceptions) ) @@ -144,8 +161,6 @@ class _slotted(object): __slots__ = ['a'] -# Do not use this tuple. It was never documented as a public API. -# It will be removed. It has no obvious signs of users on github. DescriptorTypes = ( type(_slotted.a), property, @@ -194,32 +209,37 @@ def _check_signature(func, mock, skipfirst, instance=False): sig.bind(*args, **kwargs) _copy_func_details(func, checksig) type(mock)._mock_check_sig = checksig - type(mock).__signature__ = sig def _copy_func_details(func, funcopy): + funcopy.__name__ = func.__name__ + funcopy.__doc__ = func.__doc__ + try: + funcopy.__text_signature__ = func.__text_signature__ + except AttributeError: + pass # we explicitly don't copy func.__dict__ into this copy as it would # expose original attributes that should be mocked - for attribute in ( - '__name__', '__doc__', '__text_signature__', - '__module__', '__defaults__', '__kwdefaults__', - ): - try: - setattr(funcopy, attribute, getattr(func, attribute)) - except AttributeError: - pass + try: + funcopy.__module__ = func.__module__ + except AttributeError: + pass + try: + funcopy.__defaults__ = func.__defaults__ + except AttributeError: + pass + try: + funcopy.__kwdefaults__ = func.__kwdefaults__ + except AttributeError: + pass if six.PY2: - try: - funcopy.func_defaults = func.func_defaults - except AttributeError: - pass + funcopy.func_defaults = func.func_defaults + return def _callable(obj): if isinstance(obj, ClassTypes): return True - if isinstance(obj, (staticmethod, classmethod, MethodType)): - return _callable(obj.__func__) if getattr(obj, '__call__', None) is not None: return True return False @@ -260,11 +280,13 @@ def _set_signature(mock, original, instance=False): # creates a function with signature (*args, **kwargs) that delegates to a # mock. It still does signature checking by calling a lambda with the same # signature as the original. + if not _callable(original): + return skipfirst = isinstance(original, ClassTypes) result = _get_signature_object(original, instance, skipfirst) if result is None: - return mock + return func, sig = result def checksig(*args, **kwargs): sig.bind(*args, **kwargs) @@ -279,19 +301,17 @@ def _set_signature(mock, original, instance=False): return mock(*args, **kwargs)""" % name six.exec_(src, context) funcopy = context[name] - _setup_func(funcopy, mock, sig) + _setup_func(funcopy, mock) return funcopy -def _setup_func(funcopy, mock, sig): +def _setup_func(funcopy, mock): funcopy.mock = mock - def assert_called(*args, **kwargs): - return mock.assert_called(*args, **kwargs) - def assert_not_called(*args, **kwargs): - return mock.assert_not_called(*args, **kwargs) - def assert_called_once(*args, **kwargs): - return mock.assert_called_once(*args, **kwargs) + # can't use isinstance with mocks + if not _is_instance_mock(mock): + return + def assert_called_with(*args, **kwargs): return mock.assert_called_with(*args, **kwargs) def assert_called_once_with(*args, **kwargs): @@ -324,10 +344,6 @@ def _setup_func(funcopy, mock, sig): funcopy.assert_has_calls = assert_has_calls funcopy.assert_any_call = assert_any_call funcopy.reset_mock = reset_mock - funcopy.assert_called = assert_called - funcopy.assert_not_called = assert_not_called - funcopy.assert_called_once = assert_called_once - funcopy.__signature__ = sig mock._mock_delegate = funcopy @@ -344,13 +360,6 @@ class _SentinelObject(object): def __repr__(self): return 'sentinel.%s' % self.name - def __reduce__(self): - return _unpickle_sentinel, (self.name, ) - - -def _unpickle_sentinel(name): - return getattr(sentinel, name) - class _Sentinel(object): """Access attributes to return a named object, usable as a sentinel.""" @@ -376,15 +385,21 @@ class OldStyleClass: ClassType = type(OldStyleClass) +def _copy(value): + if type(value) in (dict, list, tuple, set): + return type(value)(value) + return value + + ClassTypes = (type,) if six.PY2: ClassTypes = (type, ClassType) -_allowed_names = { +_allowed_names = set(( 'return_value', '_mock_return_value', 'side_effect', '_mock_side_effect', '_mock_parent', '_mock_new_parent', '_mock_name', '_mock_new_name' -} +)) def _delegating_property(name): @@ -427,14 +442,6 @@ class _CallList(list): def _check_and_set_parent(parent, value, name, new_name): - # function passed to create_autospec will have mock - # attribute attached to which parent must be set - if isinstance(value, FunctionTypes): - try: - value = value.mock - except AttributeError: - pass - if not _is_instance_mock(value): return False if ((value._mock_name or value._mock_new_name) or @@ -462,6 +469,8 @@ def _check_and_set_parent(parent, value, name, new_name): class _MockIter(object): def __init__(self, obj): self.obj = iter(obj) + def __iter__(self): + return self def __next__(self): return next(self.obj) @@ -498,7 +507,6 @@ class NonCallableMock(Base): __dict__['_mock_name'] = name __dict__['_mock_new_name'] = _new_name __dict__['_mock_new_parent'] = _new_parent - __dict__['_mock_sealed'] = False if spec_set is not None: spec = spec_set @@ -561,7 +569,7 @@ class NonCallableMock(Base): if isinstance(spec, ClassTypes): _spec_class = spec else: - _spec_class = type(spec) + _spec_class = _get_class(spec) res = _get_signature_object(spec, _spec_as_instance, _eat_self) _spec_signature = res and res[1] @@ -635,7 +643,7 @@ class NonCallableMock(Base): side_effect = property(__get_side_effect, __set_side_effect) - def reset_mock(self, visited=None, return_value=False, side_effect=False): + def reset_mock(self, visited=None): "Restore the mock object to its initial state." if visited is None: visited = [] @@ -650,13 +658,8 @@ class NonCallableMock(Base): self.call_args_list = _CallList() self.method_calls = _CallList() - if return_value: - self._mock_return_value = DEFAULT - if side_effect: - self._mock_side_effect = None - for child in self._mock_children.values(): - if isinstance(child, _SpecState) or child is _deleted: + if isinstance(child, _SpecState): continue child.reset_mock(visited) @@ -725,7 +728,7 @@ class NonCallableMock(Base): return result - def _extract_mock_name(self): + def __repr__(self): _name_list = [self._mock_new_name] _parent = self._mock_new_parent last = self @@ -733,7 +736,7 @@ class NonCallableMock(Base): dot = '.' if _name_list == ['()']: dot = '' - + seen = set() while _parent is not None: last = _parent @@ -744,16 +747,18 @@ class NonCallableMock(Base): _parent = _parent._mock_new_parent + # use ids here so as not to call __hash__ on the mocks + if id(_parent) in seen: + break + seen.add(id(_parent)) + _name_list = list(reversed(_name_list)) _first = last._mock_name or 'mock' if len(_name_list) > 1: if _name_list[1] not in ('()', '().'): _first += '.' _name_list[0] = _first - return ''.join(_name_list) - - def __repr__(self): - name = self._extract_mock_name() + name = ''.join(_name_list) name_string = '' if name not in ('mock', 'mock.'): @@ -765,7 +770,7 @@ class NonCallableMock(Base): if self._spec_set: spec_string = ' spec_set=%r' spec_string = spec_string % self._spec_class.__name__ - return "<{}{}{} id='{}'>".format( + return "<%s%s%s id='%s'>" % ( type(self).__name__, name_string, spec_string, @@ -782,17 +787,14 @@ class NonCallableMock(Base): extras = self._mock_methods or [] from_type = dir(type(self)) from_dict = list(self.__dict__) - from_child_mocks = [ - m_name for m_name, m_value in self._mock_children.items() - if m_value is not _deleted] if mock.FILTER_DIR: # object.__dir__ is not in 2.7 from_type = [e for e in from_type if not e.startswith('_')] from_dict = [e for e in from_dict if not e.startswith('_') or _is_magic(e)] - - return sorted(set(extras + from_type + from_dict + from_child_mocks)) + return sorted(set(extras + from_type + from_dict + + list(self._mock_children))) def __setattr__(self, name, value): @@ -826,11 +828,6 @@ class NonCallableMock(Base): else: if _check_and_set_parent(self, value, name, name): self._mock_children[name] = value - - if self._mock_sealed and not hasattr(self, name): - mock_name = self._extract_mock_name()+'.'+name - raise AttributeError('Cannot set '+mock_name) - return object.__setattr__(self, name, value) @@ -842,10 +839,11 @@ class NonCallableMock(Base): # not set on the instance itself return - obj = self._mock_children.get(name, _missing) if name in self.__dict__: - _safe_super(NonCallableMock, self).__delattr__(name) - elif obj is _deleted: + object.__delattr__(self, name) + + obj = self._mock_children.get(name, _missing) + if obj is _deleted: raise AttributeError(name) if obj is not _missing: del self._mock_children[name] @@ -858,16 +856,18 @@ class NonCallableMock(Base): def _format_mock_failure_message(self, args, kwargs): - message = 'expected call not found.\nExpected: %s\nActual: %s' + message = 'Expected call: %s\nActual call: %s' expected_string = self._format_mock_call_signature(args, kwargs) call_args = self.call_args + if len(call_args) == 3: + call_args = call_args[1:] actual_string = self._format_mock_call_signature(*call_args) return message % (expected_string, actual_string) def _call_matcher(self, _call): """ - Given a call (or simply an (args, kwargs) tuple), return a + Given a call (or simply a (args, kwargs) tuple), return a comparison key suitable for matching with other calls. This is a best effort method which relies on the spec's signature, if available, or falls back on the arguments themselves. @@ -892,10 +892,8 @@ class NonCallableMock(Base): """ self = _mock_self if self.call_count != 0: - msg = ("Expected '%s' to not have been called. Called %s times.%s" - % (self._mock_name or 'mock', - self.call_count, - self._calls_repr())) + msg = ("Expected '%s' to not have been called. Called %s times." % + (self._mock_name or 'mock', self.call_count)) raise AssertionError(msg) def assert_called(_mock_self): @@ -912,10 +910,8 @@ class NonCallableMock(Base): """ self = _mock_self if not self.call_count == 1: - msg = ("Expected '%s' to have been called once. Called %s times.%s" - % (self._mock_name or 'mock', - self.call_count, - self._calls_repr())) + msg = ("Expected '%s' to have been called once. Called %s times." % + (self._mock_name or 'mock', self.call_count)) raise AssertionError(msg) def assert_called_with(_mock_self, *args, **kwargs): @@ -926,16 +922,13 @@ class NonCallableMock(Base): self = _mock_self if self.call_args is None: expected = self._format_mock_call_signature(args, kwargs) - actual = 'not called.' - error_message = ('expected call not found.\nExpected: %s\nActual: %s' - % (expected, actual)) - raise AssertionError(error_message) + raise AssertionError('Expected call: %s\nNot called' % (expected,)) def _error_message(cause): msg = self._format_mock_failure_message(args, kwargs) if six.PY2 and cause is not None: # Tack on some diagnostics for Python without __cause__ - msg = '{}\n{}'.format(msg, str(cause)) + msg = '%s\n%s' % (msg, str(cause)) return msg expected = self._call_matcher((args, kwargs)) actual = self._call_matcher(self.call_args) @@ -945,14 +938,12 @@ class NonCallableMock(Base): def assert_called_once_with(_mock_self, *args, **kwargs): - """assert that the mock was called exactly once and that that call was - with the specified arguments.""" + """assert that the mock was called exactly once and with the specified + arguments.""" self = _mock_self if not self.call_count == 1: - msg = ("Expected '%s' to be called once. Called %s times.%s" - % (self._mock_name or 'mock', - self.call_count, - self._calls_repr())) + msg = ("Expected '%s' to be called once. Called %s times." % + (self._mock_name or 'mock', self.call_count)) raise AssertionError(msg) return self.assert_called_with(*args, **kwargs) @@ -973,8 +964,8 @@ class NonCallableMock(Base): if not any_order: if expected not in all_calls: six.raise_from(AssertionError( - 'Calls not found.\nExpected: %r%s' - % (_CallList(calls), self._calls_repr(prefix="Actual")) + 'Calls not found.\nExpected: %r\n' + 'Actual: %r' % (_CallList(calls), self.mock_calls) ), cause) return @@ -988,9 +979,7 @@ class NonCallableMock(Base): not_found.append(kall) if not_found: six.raise_from(AssertionError( - '%r does not contain all of %r in its call list, ' - 'found %r instead' % (self._mock_name or 'mock', - tuple(not_found), all_calls) + '%r not all found in call list' % (tuple(not_found),) ), cause) @@ -1026,28 +1015,9 @@ class NonCallableMock(Base): klass = Mock else: klass = _type.__mro__[1] - - if self._mock_sealed: - attribute = "." + kw["name"] if "name" in kw else "()" - mock_name = self._extract_mock_name() + attribute - raise AttributeError(mock_name) - return klass(**kw) - def _calls_repr(self, prefix="Calls"): - """Renders self.mock_calls as a string. - - Example: "\nCalls: [call(1), call(2)]." - - If self.mock_calls is empty, an empty string is returned. The - output will be truncated if very long. - """ - if not self.mock_calls: - return "" - return "\n"+prefix+": "+safe_repr(self.mock_calls)+"." - - def _try_iter(obj): if obj is None: @@ -1096,68 +1066,73 @@ class CallableMixin(Base): self = _mock_self self.called = True self.call_count += 1 + _new_name = self._mock_new_name + _new_parent = self._mock_new_parent - # handle call_args _call = _Call((args, kwargs), two=True) self.call_args = _call self.call_args_list.append(_call) + self.mock_calls.append(_Call(('', args, kwargs))) - # initial stuff for method_calls: + seen = set() + skip_next_dot = _new_name == '()' do_method_calls = self._mock_parent is not None - method_call_name = self._mock_name + name = self._mock_name + while _new_parent is not None: + this_mock_call = _Call((_new_name, args, kwargs)) + if _new_parent._mock_new_name: + dot = '.' + if skip_next_dot: + dot = '' - # initial stuff for mock_calls: - mock_call_name = self._mock_new_name - is_a_call = mock_call_name == '()' - self.mock_calls.append(_Call(('', args, kwargs))) + skip_next_dot = False + if _new_parent._mock_new_name == '()': + skip_next_dot = True - # follow up the chain of mocks: - _new_parent = self._mock_new_parent - while _new_parent is not None: + _new_name = _new_parent._mock_new_name + dot + _new_name - # handle method_calls: if do_method_calls: - _new_parent.method_calls.append(_Call((method_call_name, args, kwargs))) + if _new_name == name: + this_method_call = this_mock_call + else: + this_method_call = _Call((name, args, kwargs)) + _new_parent.method_calls.append(this_method_call) + do_method_calls = _new_parent._mock_parent is not None if do_method_calls: - method_call_name = _new_parent._mock_name + '.' + method_call_name + name = _new_parent._mock_name + '.' + name - # handle mock_calls: - this_mock_call = _Call((mock_call_name, args, kwargs)) _new_parent.mock_calls.append(this_mock_call) - - if _new_parent._mock_new_name: - if is_a_call: - dot = '' - else: - dot = '.' - is_a_call = _new_parent._mock_new_name == '()' - mock_call_name = _new_parent._mock_new_name + dot + mock_call_name - - # follow the parental chain: _new_parent = _new_parent._mock_new_parent + # use ids here so as not to call __hash__ on the mocks + _new_parent_id = id(_new_parent) + if _new_parent_id in seen: + break + seen.add(_new_parent_id) + + ret_val = DEFAULT effect = self.side_effect if effect is not None: if _is_exception(effect): raise effect - elif not _callable(effect): + + if not _callable(effect): result = next(effect) if _is_exception(result): raise result - else: - result = effect(*args, **kwargs) - - if result is not DEFAULT: + if result is DEFAULT: + result = self.return_value return result - if self._mock_return_value is not DEFAULT: - return self.return_value + ret_val = effect(*args, **kwargs) - if self._mock_wraps is not None: + if (self._mock_wraps is not None and + self._mock_return_value is DEFAULT): return self._mock_wraps(*args, **kwargs) - - return self.return_value + if ret_val is DEFAULT: + ret_val = self.return_value + return ret_val @@ -1365,7 +1340,7 @@ class _patch(object): if not self.create and original is DEFAULT: raise AttributeError( - "{} does not have the attribute {!r}".format(target, name) + "%s does not have the attribute %r" % (target, name) ) return original, local @@ -1499,7 +1474,7 @@ class _patch(object): def __exit__(self, *exc_info): """Undo the patch.""" if not _is_started(self): - return + raise RuntimeError('stop called on unstarted patcher') if self.is_local and self.temp_original is not DEFAULT: setattr(self.target, self.attribute, self.temp_original) @@ -1729,6 +1704,8 @@ class _patch_dict(object): """ def __init__(self, in_dict, values=(), clear=False, **kwargs): + if isinstance(in_dict, basestring): + in_dict = _importer(in_dict) self.in_dict = in_dict # support any argument supported by dict(...) constructor self.values = dict(values) @@ -1769,8 +1746,6 @@ class _patch_dict(object): def _patch_dict(self): values = self.values - if isinstance(self.in_dict, basestring): - self.in_dict = _importer(self.in_dict) in_dict = self.in_dict clear = self.clear @@ -1848,7 +1823,7 @@ magic_methods = ( # because there is no idivmod "divmod rdivmod neg pos abs invert " "complex int float index " - "round trunc floor ceil " + "trunc floor ceil " ) numerics = ( @@ -1861,8 +1836,6 @@ right = ' '.join('r%s' % n for n in numerics.split()) extra = '' if six.PY3: extra = 'bool next ' - if sys.version_info >= (3, 6): - extra += 'fspath ' else: extra = 'unicode long nonzero oct hex truediv rtruediv ' @@ -1870,14 +1843,13 @@ else: # (as they are metaclass methods) # __del__ is not supported at all as it causes problems if it exists -_non_defaults = { +_non_defaults = set(( '__cmp__', '__getslice__', '__setslice__', '__coerce__', # <3.x '__get__', '__set__', '__delete__', '__reversed__', '__missing__', '__reduce__', '__reduce_ex__', '__getinitargs__', '__getnewargs__', '__getstate__', '__setstate__', '__getformat__', '__setformat__', '__repr__', '__dir__', '__subclasses__', '__format__', - '__getnewargs_ex__', -} +)) def _get_method(name, func): @@ -1888,26 +1860,25 @@ def _get_method(name, func): return method -_magics = { +_magics = set( '__%s__' % method for method in ' '.join([magic_methods, numerics, inplace, right, extra]).split() -} +) _all_magics = _magics | _non_defaults -_unsupported_magics = { +_unsupported_magics = set(( '__getattr__', '__setattr__', - '__init__', '__new__', '__prepare__', + '__init__', '__new__', '__prepare__' '__instancecheck__', '__subclasscheck__', '__del__' -} +)) _calculate_return_value = { '__hash__': lambda self: object.__hash__(self), '__str__': lambda self: object.__str__(self), '__sizeof__': lambda self: object.__sizeof__(self), '__unicode__': lambda self: unicode(object.__str__(self)), - '__fspath__': lambda self: type(self).__name__+'/'+self._extract_mock_name()+'/'+str(id(self)), } _return_values = { @@ -1935,18 +1906,14 @@ def _get_eq(self): ret_val = self.__eq__._mock_return_value if ret_val is not DEFAULT: return ret_val - if self is other: - return True - return NotImplemented + return self is other return __eq__ def _get_ne(self): def __ne__(other): if self.__ne__._mock_return_value is not DEFAULT: return DEFAULT - if self is other: - return False - return NotImplemented + return self is not other return __ne__ def _get_iter(self): @@ -1980,7 +1947,6 @@ def _set_return_value(mock, method, name): except AttributeError: # XXXX why do we return AttributeError here? # set it as a side_effect instead? - # Answer: it makes magic mocks work on pypy?! return_value = AttributeError(name) method.return_value = return_value return @@ -2061,6 +2027,10 @@ class MagicProxy(object): self.name = name self.parent = parent + def __call__(self, *args, **kwargs): + m = self.create_mock() + return m(*args, **kwargs) + def create_mock(self): entry = self.name parent = self.parent @@ -2105,7 +2075,7 @@ def _format_call_signature(name, args, kwargs): return item kwargs_string = ', '.join([ - '{}={!r}'.format(encode_item(key), value) for key, value in sorted(kwargs.items()) + '%s=%r' % (encode_item(key), value) for key, value in sorted(kwargs.items()) ]) if args_string: formatted_args = args_string @@ -2137,8 +2107,9 @@ class _Call(tuple): If the _Call has no name then it will match any name. """ - def __new__(cls, value=(), name='', parent=None, two=False, + def __new__(cls, value=(), name=None, parent=None, two=False, from_kall=True): + name = '' args = () kwargs = {} _len = len(value) @@ -2171,9 +2142,9 @@ class _Call(tuple): def __init__(self, value=(), name=None, parent=None, two=False, from_kall=True): - self._mock_name = name - self._mock_parent = parent - self._mock_from_kall = from_kall + self.name = name + self.parent = parent + self.from_kall = from_kall def __eq__(self, other): @@ -2190,10 +2161,6 @@ class _Call(tuple): else: self_name, self_args, self_kwargs = self - if (getattr(self, '_mock_parent', None) and getattr(other, '_mock_parent', None) - and self._mock_parent != other._mock_parent): - return False - other_name = '' if len_other == 0: other_args, other_kwargs = (), {} @@ -2237,17 +2204,17 @@ class _Call(tuple): __hash__ = None def __call__(self, *args, **kwargs): - if self._mock_name is None: + if self.name is None: return _Call(('', args, kwargs), name='()') - name = self._mock_name + '()' - return _Call((self._mock_name, args, kwargs), name=name, parent=self) + name = self.name + '()' + return _Call((self.name, args, kwargs), name=name, parent=self) def __getattr__(self, attr): - if self._mock_name is None: + if self.name is None: return _Call(name=attr, from_kall=False) - name = '{}.{}'.format(self._mock_name, attr) + name = '%s.%s' % (self.name, attr) return _Call(name=name, parent=self, from_kall=False) @@ -2257,25 +2224,9 @@ class _Call(tuple): def index(self, *args, **kwargs): return self.__getattr__('index')(*args, **kwargs) - def _get_call_arguments(self): - if len(self) == 2: - args, kwargs = self - else: - name, args, kwargs = self - - return args, kwargs - - @property - def args(self): - return self._get_call_arguments()[0] - - @property - def kwargs(self): - return self._get_call_arguments()[1] - def __repr__(self): - if not self._mock_from_kall: - name = self._mock_name or 'call' + if not self.from_kall: + name = self.name or 'call' if name.startswith('()'): name = 'call%s' % name return name @@ -2301,9 +2252,9 @@ class _Call(tuple): vals = [] thing = self while thing is not None: - if thing._mock_from_kall: + if thing.from_kall: vals.append(thing) - thing = thing._mock_parent + thing = thing.parent return _CallList(reversed(vals)) @@ -2349,7 +2300,7 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None, _kwargs.update(kwargs) Klass = MagicMock - if inspect.isdatadescriptor(spec): + if type(spec) in DescriptorTypes: # descriptors don't have a spec # because we don't know what type they return _kwargs = {} @@ -2383,12 +2334,6 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None, _name='()', _parent=mock) for entry in dir(spec): - - # This are __ and so treated as magic on Py3, on Py2 we need to - # explicitly ignore them: - if six.PY2 and (entry.startswith('im_') or entry.startswith('func_')): - continue - if _is_magic(entry): # MagicMock already does the useful magic methods for us continue @@ -2464,10 +2409,19 @@ def _must_skip(spec, entry, is_type): else: return False - # function is a dynamically provided attribute + # shouldn't get here unless function is a dynamically provided attribute + # XXXX untested behaviour return is_type +def _get_class(obj): + try: + return obj.__class__ + except AttributeError: + # it is possible for objects to have no __class__ + return type(obj) + + class _SpecState(object): def __init__(self, spec, spec_set=False, parent=None, @@ -2494,13 +2448,25 @@ MethodWrapperTypes = ( file_spec = None - -def _to_stream(read_data): - if isinstance(read_data, bytes): - return io.BytesIO(read_data) +def _iterate_read_data(read_data): + # Helper for mock_open: + # Retrieve lines from read_data via a generator so that separate calls to + # readline, read, and readlines are properly interleaved + sep = b'\n' if isinstance(read_data, bytes) else '\n' + data_as_list = [l + sep for l in read_data.split(sep)] + + if data_as_list[-1] == sep: + # If the last line ended in a newline, the list comprehension will have an + # extra entry that's just a newline. Remove this. + data_as_list = data_as_list[:-1] else: - return io.StringIO(read_data) + # If there wasn't an extra newline by itself, then the file being + # emulated doesn't have a newline to end the last line remove the + # newline that our naive format() added + data_as_list[-1] = data_as_list[-1][:-1] + for line in data_as_list: + yield line def mock_open(mock=None, read_data=''): """ @@ -2511,35 +2477,27 @@ def mock_open(mock=None, read_data=''): default) then a `MagicMock` will be created for you, with the API limited to methods or attributes available on standard file handles. - `read_data` is a string for the `read`, `readline` and `readlines` of the + `read_data` is a string for the `read` methoddline`, and `readlines` of the file handle to return. This is an empty string by default. """ - _read_data = _to_stream(read_data) - _state = [_read_data, None] - def _readlines_side_effect(*args, **kwargs): if handle.readlines.return_value is not None: return handle.readlines.return_value - return _state[0].readlines(*args, **kwargs) + return list(_state[0]) def _read_side_effect(*args, **kwargs): if handle.read.return_value is not None: return handle.read.return_value - return _state[0].read(*args, **kwargs) - - def _readline_side_effect(*args, **kwargs): - for item in _iter_side_effect(): - yield item - while True: - yield _state[0].readline(*args, **kwargs) + return type(read_data)().join(_state[0]) - def _iter_side_effect(): + def _readline_side_effect(): if handle.readline.return_value is not None: while True: yield handle.readline.return_value for line in _state[0]: yield line + global file_spec if file_spec is None: # set on first use @@ -2555,6 +2513,8 @@ def mock_open(mock=None, read_data=''): handle = MagicMock(spec=file_spec) handle.__enter__.return_value = handle + _state = [_iterate_read_data(read_data), None] + handle.write.return_value = None handle.read.return_value = None handle.readline.return_value = None @@ -2564,10 +2524,9 @@ def mock_open(mock=None, read_data=''): _state[1] = _readline_side_effect() handle.readline.side_effect = _state[1] handle.readlines.side_effect = _readlines_side_effect - handle.__iter__.side_effect = _iter_side_effect def reset_data(*args, **kwargs): - _state[0] = _to_stream(read_data) + _state[0] = _iterate_read_data(read_data) if handle.readline.side_effect == _state[1]: # Only reset the side effect if the user hasn't overridden it. _state[1] = _readline_side_effect() @@ -2595,25 +2554,3 @@ class PropertyMock(Mock): return self() def __set__(self, obj, val): self(val) - - -def seal(mock): - """Disable the automatic generation of child mocks. - - Given an input Mock, seals it to ensure no further mocks will be generated - when accessing an attribute that was not already defined. - - The operation recursively seals the mock passed in, meaning that - the mock itself, any mocks generated by accessing one of its attributes, - and all assigned mocks without a name or spec will be sealed. - """ - mock._mock_sealed = True - for attr in dir(mock): - try: - m = getattr(mock, attr) - except AttributeError: - continue - if not isinstance(m, NonCallableMock): - continue - if m._mock_new_parent is mock: - seal(m) diff --git a/mock/tests/conftest.py b/mock/tests/conftest.py deleted file mode 100644 index 78831f6..0000000 --- a/mock/tests/conftest.py +++ /dev/null @@ -1,6 +0,0 @@ -import six - - -def pytest_ignore_collect(path): - if 'py3' in path.basename and six.PY2: - return True diff --git a/mock/tests/support.py b/mock/tests/support.py index d57a372..c7ad20b 100644 --- a/mock/tests/support.py +++ b/mock/tests/support.py @@ -1,10 +1,3 @@ -import contextlib -import sys - - -target = {'foo': 'FOO'} - - def is_instance(obj, klass): """Version of is_instance that doesn't access __class__""" return issubclass(type(obj), klass) @@ -13,34 +6,9 @@ def is_instance(obj, klass): class SomeClass(object): class_attribute = None - def wibble(self): pass + def wibble(self): + pass class X(object): pass - - -@contextlib.contextmanager -def uncache(*names): - """Uncache a module from sys.modules. - - A basic sanity check is performed to prevent uncaching modules that either - cannot/shouldn't be uncached. - - """ - for name in names: - if name in ('sys', 'marshal', 'imp'): - raise ValueError( - "cannot uncache {0}".format(name)) - try: - del sys.modules[name] - except KeyError: - pass - try: - yield - finally: - for name in names: - try: - del sys.modules[name] - except KeyError: - pass diff --git a/mock/tests/testcallable.py b/mock/tests/testcallable.py index 729947e..10acdc3 100644 --- a/mock/tests/testcallable.py +++ b/mock/tests/testcallable.py @@ -2,7 +2,7 @@ # E-mail: fuzzyman AT voidspace DOT org DOT uk # http://www.voidspace.org.uk/python/mock/ -import unittest +import unittest2 as unittest from mock.tests.support import is_instance, X, SomeClass from mock import ( @@ -27,7 +27,7 @@ class TestCallable(unittest.TestCase): self.assertIn(mock.__class__.__name__, repr(mock)) - def test_hierarchy(self): + def test_heirarchy(self): self.assertTrue(issubclass(MagicMock, Mock)) self.assertTrue(issubclass(NonCallableMagicMock, NonCallableMock)) @@ -98,7 +98,8 @@ class TestCallable(unittest.TestCase): def test_patch_spec_callable_class(self): class CallableX(X): - def __call__(self): pass + def __call__(self): + pass class Sub(CallableX): pass @@ -107,7 +108,8 @@ class TestCallable(unittest.TestCase): pass class OldStyle: - def __call__(self): pass + def __call__(self): + pass class OldStyleSub(OldStyle): pass diff --git a/mock/tests/testhelpers.py b/mock/tests/testhelpers.py index d56a47f..a87df1b 100644 --- a/mock/tests/testhelpers.py +++ b/mock/tests/testhelpers.py @@ -1,32 +1,25 @@ # Copyright (C) 2007-2012 Michael Foord & the mock team # E-mail: fuzzyman AT voidspace DOT org DOT uk # http://www.voidspace.org.uk/python/mock/ -import socket -import inspect import six -import sys -import time -import unittest +import unittest2 as unittest from mock import ( call, create_autospec, MagicMock, Mock, ANY, patch, PropertyMock ) -from mock.mock import _Call, _CallList, _callable +from mock.mock import _Call, _CallList from datetime import datetime -from functools import partial - - -if six.PY2: - import funcsigs - class SomeClass(object): - def one(self, a, b): pass - def two(self): pass - def three(self, a=None): pass + def one(self, a, b): + pass + def two(self): + pass + def three(self, a=None): + pass @@ -57,9 +50,12 @@ class AnyTest(unittest.TestCase): def test_any_mock_calls_comparison_order(self): mock = Mock() + d = datetime.now() class Foo(object): - def __eq__(self, other): pass - def __ne__(self, other): pass + def __eq__(self, other): + return False + def __ne__(self, other): + return True for d in datetime.now(), Foo(): mock.reset_mock() @@ -152,8 +148,6 @@ class CallTest(unittest.TestCase): self.assertEqual(args, ('foo', (1, 2, 3))) self.assertEqual(args, ('foo', (1, 2, 3), {})) self.assertEqual(args, ((1, 2, 3), {})) - self.assertEqual(args.args, (1, 2, 3)) - self.assertEqual(args.kwargs, {}) def test_named_call_with_args(self): @@ -161,8 +155,6 @@ class CallTest(unittest.TestCase): self.assertEqual(args, ('foo', (1, 2, 3))) self.assertEqual(args, ('foo', (1, 2, 3), {})) - self.assertEqual(args.args, (1, 2, 3)) - self.assertEqual(args.kwargs, {}) self.assertNotEqual(args, ((1, 2, 3),)) self.assertNotEqual(args, ((1, 2, 3), {})) @@ -175,8 +167,6 @@ class CallTest(unittest.TestCase): self.assertEqual(args, ('foo', dict(a=3, b=4))) self.assertEqual(args, ('foo', (), dict(a=3, b=4))) self.assertEqual(args, ((), dict(a=3, b=4))) - self.assertEqual(args.args, ()) - self.assertEqual(args.kwargs, dict(a=3, b=4)) def test_named_call_with_kwargs(self): @@ -184,8 +174,6 @@ class CallTest(unittest.TestCase): self.assertEqual(args, ('foo', dict(a=3, b=4))) self.assertEqual(args, ('foo', (), dict(a=3, b=4))) - self.assertEqual(args.args, ()) - self.assertEqual(args.kwargs, dict(a=3, b=4)) self.assertNotEqual(args, (dict(a=3, b=4),)) self.assertNotEqual(args, ((), dict(a=3, b=4))) @@ -193,7 +181,6 @@ class CallTest(unittest.TestCase): def test_call_with_args_call_empty_name(self): args = _Call(((1, 2, 3), {})) - self.assertEqual(args, call(1, 2, 3)) self.assertEqual(call(1, 2, 3), args) self.assertIn(call(1, 2, 3), [args]) @@ -286,22 +273,6 @@ class CallTest(unittest.TestCase): self.assertEqual(mock.mock_calls, last_call.call_list()) - def test_extended_not_equal(self): - a = call(x=1).foo - b = call(x=2).foo - self.assertEqual(a, a) - self.assertEqual(b, b) - self.assertNotEqual(a, b) - - - def test_nested_calls_not_equal(self): - a = call(x=1).foo().bar - b = call(x=2).foo().bar - self.assertEqual(a, a) - self.assertEqual(b, b) - self.assertNotEqual(a, b) - - def test_call_list(self): mock = MagicMock() mock(1) @@ -341,11 +312,6 @@ class CallTest(unittest.TestCase): other_args = _Call(((1, 2), {'a': 3})) self.assertEqual(args, other_args) - def test_call_with_name(self): - self.assertEqual(_Call((), 'foo')[0], 'foo') - self.assertEqual(_Call((('bar', 'barz'),),)[0], '') - self.assertEqual(_Call((('bar', 'barz'), {'hello': 'world'}),)[0], '') - class SpecSignatureTest(unittest.TestCase): @@ -384,7 +350,8 @@ class SpecSignatureTest(unittest.TestCase): def test_create_autospec_return_value(self): - def f(): pass + def f(): + pass mock = create_autospec(f, return_value='foo') self.assertEqual(mock(), 'foo') @@ -404,7 +371,8 @@ class SpecSignatureTest(unittest.TestCase): def test_mocking_unbound_methods(self): class Foo(object): - def foo(self, foo): pass + def foo(self, foo): + pass p = patch.object(Foo, 'foo') mock_foo = p.start() Foo().foo(1) @@ -412,6 +380,23 @@ class SpecSignatureTest(unittest.TestCase): mock_foo.assert_called_with(1) + @unittest.expectedFailure + def test_create_autospec_unbound_methods(self): + # see mock issue 128 + class Foo(object): + def foo(self): + pass + + klass = create_autospec(Foo) + instance = klass() + self.assertRaises(TypeError, instance.foo, 1) + + # Note: no type checking on the "self" parameter + klass.foo(1) + klass.foo.assert_called_with(1) + self.assertRaises(TypeError, klass.foo) + + def test_create_autospec_keyword_arguments(self): class Foo(object): a = 3 @@ -420,7 +405,7 @@ class SpecSignatureTest(unittest.TestCase): @unittest.skipUnless(six.PY3, "Keyword only arguments Python 3 specific") def test_create_autospec_keyword_only_arguments(self): - func_def = "def foo(a, *, b=None): pass\n" + func_def = "def foo(a, *, b=None):\n pass\n" namespace = {} exec (func_def, namespace) foo = namespace['foo'] @@ -435,7 +420,8 @@ class SpecSignatureTest(unittest.TestCase): def test_function_as_instance_attribute(self): obj = SomeClass() - def f(a): pass + def f(a): + pass obj.f = f mock = create_autospec(obj) @@ -471,56 +457,13 @@ class SpecSignatureTest(unittest.TestCase): self._check_someclass_mock(mock) - @unittest.skipIf('PyPy' in sys.version, - "This fails on pypy, " - "see https://github.com/testing-cabal/mock/issues/452") - def test_spec_has_descriptor_returning_function(self): - class CrazyDescriptor(object): - def __get__(self, obj, type_): - if obj is None: - return lambda x: None - - class MyClass(object): - some_attr = CrazyDescriptor() - - mock = create_autospec(MyClass) - mock.some_attr(1) - with self.assertRaises(TypeError): - mock.some_attr() - with self.assertRaises(TypeError): - mock.some_attr(1, 2) - - @unittest.skipIf(six.PY2, "object.__dir__ doesn't exist in Python 2") - def test_spec_has_function_not_in_bases(self): - class CrazyClass(object): - def __dir__(self): - return super(CrazyClass, self).__dir__() + ['crazy'] - - def __getattr__(self, item): - if item == 'crazy': - return lambda x: x - raise AttributeError(item) - - inst = CrazyClass() - with self.assertRaises(AttributeError): - inst.other - self.assertEqual(inst.crazy(42), 42) - mock = create_autospec(inst) - mock.crazy(42) - with self.assertRaises(TypeError): - mock.crazy() - with self.assertRaises(TypeError): - mock.crazy(1, 2) - - - @unittest.skipIf('PyPy' in sys.version and sys.version_info < (3, 0), - "Fails on pypy2 due to incorrect signature for dict.pop from funcsigs") def test_builtin_functions_types(self): # we could replace builtin functions / methods with a function # with *args / **kwargs signature. Using the builtin method type # as a spec seems to work fairly well though. class BuiltinSubclass(list): - def bar(self, arg): pass + def bar(self, arg): + pass sorted = sorted attr = {} @@ -594,13 +537,17 @@ class SpecSignatureTest(unittest.TestCase): def test_descriptors(self): class Foo(object): @classmethod - def f(cls, a, b): pass + def f(cls, a, b): + pass @staticmethod - def g(a, b): pass + def g(a, b): + pass - class Bar(Foo): pass + class Bar(Foo): + pass - class Baz(SomeClass, Bar): pass + class Baz(SomeClass, Bar): + pass for spec in (Foo, Foo(), Bar, Bar(), Baz, Baz()): mock = create_autospec(spec) @@ -614,7 +561,8 @@ class SpecSignatureTest(unittest.TestCase): @unittest.skipIf(six.PY3, "No old style classes in Python 3") def test_old_style_classes(self): class Foo: - def f(self, a, b): pass + def f(self, a, b): + pass class Bar(Foo): g = Foo() @@ -634,7 +582,8 @@ class SpecSignatureTest(unittest.TestCase): def test_recursive(self): class A(object): - def a(self): pass + def a(self): + pass foo = 'foo bar baz' bar = foo @@ -656,9 +605,11 @@ class SpecSignatureTest(unittest.TestCase): def test_spec_inheritance_for_classes(self): class Foo(object): - def a(self, x): pass + def a(self, x): + pass class Bar(object): - def f(self, y): pass + def f(self, y): + pass class_mock = create_autospec(Foo) @@ -738,7 +689,8 @@ class SpecSignatureTest(unittest.TestCase): def test_function(self): - def f(a, b): pass + def f(a, b): + pass mock = create_autospec(f) self.assertRaises(TypeError, mock) @@ -768,10 +720,9 @@ class SpecSignatureTest(unittest.TestCase): def existing(a, b): return a + b - self.assertEqual(RaiserClass.existing(1, 2), 3) s = create_autospec(RaiserClass) self.assertRaises(TypeError, lambda x: s.existing(1, 2, 3)) - self.assertEqual(s.existing(1, 2), s.existing.return_value) + s.existing(1, 2) self.assertRaises(AttributeError, lambda: s.nonexisting) # check we can fetch the raiser attribute and it has no spec @@ -781,7 +732,8 @@ class SpecSignatureTest(unittest.TestCase): def test_signature_class(self): class Foo(object): - def __init__(self, a, b=3): pass + def __init__(self, a, b=3): + pass mock = create_autospec(Foo) @@ -796,7 +748,8 @@ class SpecSignatureTest(unittest.TestCase): @unittest.skipIf(six.PY3, 'no old style classes in Python 3') def test_signature_old_style_class(self): class Foo: - def __init__(self, a, b=3): pass + def __init__(self, a, b=3): + pass mock = create_autospec(Foo) @@ -831,8 +784,10 @@ class SpecSignatureTest(unittest.TestCase): def test_signature_callable(self): class Callable(object): - def __init__(self, x, y): pass - def __call__(self, a): pass + def __init__(self, x, y): + pass + def __call__(self, a): + pass mock = create_autospec(Callable) mock(1, 2) @@ -888,7 +843,8 @@ class SpecSignatureTest(unittest.TestCase): def test_autospec_functions_with_self_in_odd_place(self): class Foo(object): - def f(a, self): pass + def f(a, self): + pass a = create_autospec(Foo) a.f(10) @@ -902,7 +858,8 @@ class SpecSignatureTest(unittest.TestCase): def test_autospec_property(self): class Foo(object): @property - def foo(self): pass + def foo(self): + return 3 foo = create_autospec(Foo) mock_property = foo.foo @@ -929,140 +886,6 @@ class SpecSignatureTest(unittest.TestCase): mock_slot.abc.assert_called_once_with(4, 5, 6) - def test_autospec_data_descriptor(self): - class Descriptor(object): - def __init__(self, value): - self.value = value - - def __get__(self, obj, cls=None): - return self - - def __set__(self, obj, value): pass - - class MyProperty(property): - pass - - class Foo(object): - __slots__ = ['slot'] - - @property - def prop(self): pass - - @MyProperty - def subprop(self): pass - - desc = Descriptor(42) - - foo = create_autospec(Foo) - - def check_data_descriptor(mock_attr): - # Data descriptors don't have a spec. - self.assertIsInstance(mock_attr, MagicMock) - mock_attr(1, 2, 3) - mock_attr.abc(4, 5, 6) - mock_attr.assert_called_once_with(1, 2, 3) - mock_attr.abc.assert_called_once_with(4, 5, 6) - - # property - check_data_descriptor(foo.prop) - # property subclass - check_data_descriptor(foo.subprop) - # class __slot__ - check_data_descriptor(foo.slot) - # plain data descriptor - check_data_descriptor(foo.desc) - - - @unittest.skipIf('PyPy' in sys.version and sys.version_info > (3, 0), - "See https://github.com/testing-cabal/mock/issues/452") - def test_autospec_on_bound_builtin_function(self): - meth = six.create_bound_method(time.ctime, time.time()) - self.assertIsInstance(meth(), str) - mocked = create_autospec(meth) - - # no signature, so no spec to check against - mocked() - mocked.assert_called_once_with() - mocked.reset_mock() - mocked(4, 5, 6) - mocked.assert_called_once_with(4, 5, 6) - - def test_autospec_socket(self): - sock_class = create_autospec(socket.socket) - self.assertRaises(TypeError, sock_class, foo=1) - - - def test_autospec_getattr_partial_function(self): - # bpo-32153 : getattr returning partial functions without - # __name__ should not create AttributeError in create_autospec - class Foo(object): - def __getattr__(self, attribute): - return partial(lambda name: name, attribute) - proxy = Foo() - autospec = create_autospec(proxy) - self.assertFalse(hasattr(autospec, '__name__')) - - - def test_spec_inspect_signature(self): - - def myfunc(x, y): pass - - mock = create_autospec(myfunc) - mock(1, 2) - mock(x=1, y=2) - - if six.PY2: - self.assertEqual(funcsigs.signature(mock), funcsigs.signature(myfunc)) - else: - self.assertEqual(inspect.getfullargspec(mock), inspect.getfullargspec(myfunc)) - self.assertEqual(mock.mock_calls, [call(1, 2), call(x=1, y=2)]) - self.assertRaises(TypeError, mock, 1) - - - def test_spec_function_no_name(self): - func = lambda: 'nope' - mock = create_autospec(func) - self.assertEqual(mock.__name__, 'funcopy') - - - @unittest.skipIf(six.PY3, "Here to test our Py2 _isidentifier") - def test_spec_function_has_identifier_name(self): - func = lambda: 'nope' - func.__name__ = 'global' - mock = create_autospec(func) - self.assertEqual(mock.__name__, 'funcopy') - - - def test_spec_function_assert_has_calls(self): - def f(a): pass - mock = create_autospec(f) - mock(1) - mock.assert_has_calls([call(1)]) - with self.assertRaises(AssertionError): - mock.assert_has_calls([call(2)]) - - - def test_spec_function_assert_any_call(self): - def f(a): pass - mock = create_autospec(f) - mock(1) - mock.assert_any_call(1) - with self.assertRaises(AssertionError): - mock.assert_any_call(2) - - - def test_spec_function_reset_mock(self): - def f(a): pass - rv = Mock() - mock = create_autospec(f, return_value=rv) - mock(1)(2) - self.assertEqual(mock.mock_calls, [call(1)]) - self.assertEqual(rv.mock_calls, [call(2)]) - mock.reset_mock() - self.assertEqual(mock.mock_calls, []) - self.assertEqual(rv.mock_calls, []) - - class TestCallList(unittest.TestCase): def test_args_list_contains_call_list(self): @@ -1148,40 +971,5 @@ class TestCallList(unittest.TestCase): self.assertNotIsInstance(returned, PropertyMock) -class TestCallablePredicate(unittest.TestCase): - - def test_type(self): - for obj in [str, bytes, int, list, tuple, SomeClass]: - self.assertTrue(_callable(obj)) - - def test_call_magic_method(self): - class Callable: - def __call__(self): pass - instance = Callable() - self.assertTrue(_callable(instance)) - - def test_staticmethod(self): - class WithStaticMethod: - @staticmethod - def staticfunc(): pass - self.assertTrue(_callable(WithStaticMethod.staticfunc)) - - def test_non_callable_staticmethod(self): - class BadStaticMethod: - not_callable = staticmethod(None) - self.assertFalse(_callable(BadStaticMethod.not_callable)) - - def test_classmethod(self): - class WithClassMethod: - @classmethod - def classfunc(cls): pass - self.assertTrue(_callable(WithClassMethod.classfunc)) - - def test_non_callable_classmethod(self): - class BadClassMethod: - not_callable = classmethod(None) - self.assertFalse(_callable(BadClassMethod.not_callable)) - - if __name__ == '__main__': unittest.main() diff --git a/mock/tests/testhelpers_py3.py b/mock/tests/testhelpers_py3.py deleted file mode 100644 index 64d62f8..0000000 --- a/mock/tests/testhelpers_py3.py +++ /dev/null @@ -1,23 +0,0 @@ -import inspect -import unittest - -from mock import call, create_autospec - - -class CallTest(unittest.TestCase): - - - def test_spec_inspect_signature_annotations(self): - - def foo(a: int, b: int=10, *, c:int) -> int: - return a + b + c - - self.assertEqual(foo(1, 2, c=3), 6) - mock = create_autospec(foo) - mock(1, 2, c=3) - mock(1, c=3) - - self.assertEqual(inspect.getfullargspec(mock), inspect.getfullargspec(foo)) - self.assertEqual(mock.mock_calls, [call(1, 2, c=3), call(1, c=3)]) - self.assertRaises(TypeError, mock, 1) - self.assertRaises(TypeError, mock, 1, 2, 3, c=4) diff --git a/mock/tests/testmagicmethods.py b/mock/tests/testmagicmethods.py index f6c25fb..f47a202 100644 --- a/mock/tests/testmagicmethods.py +++ b/mock/tests/testmagicmethods.py @@ -11,13 +11,12 @@ except NameError: unicode = str long = int -import math -import os +import inspect import sys import textwrap -import unittest import six +import unittest2 as unittest from mock import Mock, MagicMock from mock.mock import _magics @@ -303,7 +302,7 @@ class TestMockingMagicMethods(unittest.TestCase): for entry in _magics: self.assertTrue(hasattr(mock, entry)) - self.assertFalse(hasattr(mock, '__imaginary__')) + self.assertFalse(hasattr(mock, '__imaginery__')) def test_magic_mock_equality(self): @@ -331,16 +330,6 @@ class TestMockingMagicMethods(unittest.TestCase): self.assertEqual(unicode(mock), object.__str__(mock)) self.assertIsInstance(unicode(mock), unicode) self.assertTrue(bool(mock)) - self.assertEqual(math.trunc(mock), mock.__trunc__()) - if six.PY2: - # These fall back to __float__ in Python 2: - self.assertEqual(round(mock), 1.0) - self.assertEqual(math.floor(mock), 1.0) - self.assertEqual(math.ceil(mock), 1.0) - else: - self.assertEqual(round(mock), mock.__round__()) - self.assertEqual(math.floor(mock), mock.__floor__()) - self.assertEqual(math.ceil(mock), mock.__ceil__()) if six.PY2: self.assertEqual(oct(mock), '1') else: @@ -362,20 +351,10 @@ class TestMockingMagicMethods(unittest.TestCase): self.assertEqual(mock, object()) - def test_magic_methods_fspath(self): - mock = MagicMock() - if sys.version_info < (3, 6): - self.assertRaises(AttributeError, lambda: mock.__fspath__) - else: - expected_path = mock.__fspath__() - mock.reset_mock() - self.assertEqual(os.fspath(mock), expected_path) - mock.__fspath__.assert_called_once() - - def test_magic_methods_and_spec(self): class Iterable(object): - def __iter__(self): pass + def __iter__(self): + pass mock = Mock(spec=Iterable) self.assertRaises(AttributeError, lambda: mock.__iter__) @@ -399,7 +378,8 @@ class TestMockingMagicMethods(unittest.TestCase): def test_magic_methods_and_spec_set(self): class Iterable(object): - def __iter__(self): pass + def __iter__(self): + pass mock = Mock(spec_set=Iterable) self.assertRaises(AttributeError, lambda: mock.__iter__) @@ -425,7 +405,7 @@ class TestMockingMagicMethods(unittest.TestCase): mock = MagicMock() def set_setattr(): mock.__setattr__ = lambda self, name: None - self.assertRaisesRegexp(AttributeError, + self.assertRaisesRegex(AttributeError, "Attempting to set unsupported magic method '__setattr__'.", set_setattr ) @@ -535,7 +515,7 @@ class TestMockingMagicMethods(unittest.TestCase): self.assertIsInstance(bar_direct, MagicMock) # http://bugs.python.org/issue23310 - # Check if you can change behaviour of magic methods in MagicMock init + # Check if you can change behaviour of magic methds in MagicMock init def test_magic_in_initialization(self): m = MagicMock(**{'__str__.return_value': "12"}) self.assertEqual(str(m), "12") diff --git a/mock/tests/testmock.py b/mock/tests/testmock.py index 5f6045a..66323e9 100644 --- a/mock/tests/testmock.py +++ b/mock/tests/testmock.py @@ -4,20 +4,20 @@ import copy import pickle -import re import sys import tempfile import six -import unittest +import unittest2 as unittest import mock -from mock.mock import ( +from mock import ( call, DEFAULT, patch, sentinel, MagicMock, Mock, NonCallableMock, - NonCallableMagicMock, _Call, _CallList, + NonCallableMagicMock, create_autospec ) +from mock.mock import _CallList from mock.tests.support import is_instance @@ -41,13 +41,16 @@ class Iter(object): class Something(object): - def meth(self, a, b, c, d=None): pass + def meth(self, a, b, c, d=None): + pass @classmethod - def cmeth(cls, a, b, c, d=None): pass + def cmeth(cls, a, b, c, d=None): + pass @staticmethod - def smeth(a, b, c, d=None): pass + def smeth(a, b, c, d=None): + pass class Subclass(MagicMock): @@ -103,21 +106,6 @@ class MockTest(unittest.TestCase): "return value in constructor not honoured") - def test_change_return_value_via_delegate(self): - def f(): pass - mock = create_autospec(f) - mock.mock.return_value = 1 - self.assertEqual(mock(), 1) - - - def test_change_side_effect_via_delegate(self): - def f(): pass - mock = create_autospec(f) - mock.mock.side_effect = TypeError() - with self.assertRaises(TypeError): - mock() - - def test_repr(self): mock = Mock(name='foo') self.assertIn('foo', repr(mock)) @@ -196,7 +184,8 @@ class MockTest(unittest.TestCase): results = [1, 2, 3] def effect(): return results.pop() - def f(): pass + def f(): + pass mock = create_autospec(f) mock.side_effect = [1, 2, 3] @@ -211,11 +200,28 @@ class MockTest(unittest.TestCase): def test_autospec_side_effect_exception(self): # Test for issue 23661 - def f(): pass + def f(): + pass mock = create_autospec(f) mock.side_effect = ValueError('Bazinga!') - self.assertRaisesRegexp(ValueError, 'Bazinga!', mock) + self.assertRaisesRegex(ValueError, 'Bazinga!', mock) + + @unittest.skipUnless('java' in sys.platform, + 'This test only applies to Jython') + def test_java_exception_side_effect(self): + import java + mock = Mock(side_effect=java.lang.RuntimeException("Boom!")) + + # can't use assertRaises with java exceptions + try: + mock(1, 2, fish=3) + except java.lang.RuntimeException: + pass + else: + self.fail('java exception not raised') + mock.assert_called_with(1,2, fish=3) + def test_reset_mock(self): parent = Mock() @@ -284,10 +290,6 @@ class MockTest(unittest.TestCase): self.assertEqual(mock.call_count, 1, "call_count incoreect") self.assertEqual(mock.call_args, ((sentinel.Arg,), {}), "call_args not set") - self.assertEqual(mock.call_args.args, (sentinel.Arg,), - "call_args not set") - self.assertEqual(mock.call_args.kwargs, {}, - "call_args not set") self.assertEqual(mock.call_args_list, [((sentinel.Arg,), {})], "call_args_list not initialised correctly") @@ -321,35 +323,11 @@ class MockTest(unittest.TestCase): ]) self.assertEqual(mock.call_args, ((sentinel.Arg,), {"kw": sentinel.Kwarg})) - self.assertEqual(mock.call_args.args, (sentinel.Arg,)) - self.assertEqual(mock.call_args.kwargs, {"kw": sentinel.Kwarg}) # Comparing call_args to a long sequence should not raise # an exception. See issue 24857. self.assertFalse(mock.call_args == "a long sequence") - - def test_calls_equal_with_any(self): - # Check that equality and non-equality is consistent even when - # comparing with mock.ANY - mm = mock.MagicMock() - self.assertTrue(mm == mm) - self.assertFalse(mm != mm) - self.assertFalse(mm == mock.MagicMock()) - self.assertTrue(mm != mock.MagicMock()) - self.assertTrue(mm == mock.ANY) - self.assertFalse(mm != mock.ANY) - self.assertTrue(mock.ANY == mm) - self.assertFalse(mock.ANY != mm) - - call1 = mock.call(mock.MagicMock()) - call2 = mock.call(mock.ANY) - self.assertTrue(call1 == call2) - self.assertFalse(call1 != call2) - self.assertTrue(call2 == call1) - self.assertFalse(call2 != call1) - - def test_assert_called_with(self): mock = Mock() mock() @@ -365,14 +343,9 @@ class MockTest(unittest.TestCase): mock.assert_called_with(1, 2, 3, a='fish', b='nothing') - def test_assert_called_with_any(self): - m = MagicMock() - m(MagicMock()) - m.assert_called_with(mock.ANY) - - def test_assert_called_with_function_spec(self): - def f(a, b, c, d=None): pass + def f(a, b, c, d=None): + pass mock = Mock(spec=f) @@ -431,17 +404,10 @@ class MockTest(unittest.TestCase): lambda: mock.assert_called_once_with('bob', 'bar', baz=2) ) - def test_assert_called_once_with_call_list(self): - m = Mock() - m(1) - m(2) - self.assertRaisesRegexp(AssertionError, - re.escape("Calls: [call(1), call(2)]"), - lambda: m.assert_called_once_with(2)) - def test_assert_called_once_with_function_spec(self): - def f(a, b, c, d=None): pass + def f(a, b, c, d=None): + pass mock = Mock(spec=f) @@ -535,7 +501,7 @@ class MockTest(unittest.TestCase): # this should be allowed mock.something - self.assertRaisesRegexp( + self.assertRaisesRegex( AttributeError, "Mock object has no attribute 'something_else'", getattr, mock, 'something_else' @@ -546,19 +512,20 @@ class MockTest(unittest.TestCase): class Something(object): x = 3 __something__ = None - def y(self): pass + def y(self): + pass def test_attributes(mock): # should work mock.x mock.y mock.__something__ - self.assertRaisesRegexp( + self.assertRaisesRegex( AttributeError, "Mock object has no attribute 'z'", getattr, mock, 'z' ) - self.assertRaisesRegexp( + self.assertRaisesRegex( AttributeError, "Mock object has no attribute '__foobar__'", getattr, mock, '__foobar__' @@ -580,16 +547,6 @@ class MockTest(unittest.TestCase): real.assert_called_with(1, 2, fish=3) - def test_wraps_prevents_automatic_creation_of_mocks(self): - class Real(object): - pass - - real = Real() - mock = Mock(wraps=real) - - self.assertRaises(AttributeError, lambda: mock.new_attr()) - - def test_wraps_call_with_nondefault_return_value(self): real = Mock() @@ -616,110 +573,6 @@ class MockTest(unittest.TestCase): self.assertEqual(result, Real.attribute.frog()) - def test_customize_wrapped_object_with_side_effect_iterable_with_default(self): - class Real(object): - def method(self): - return sentinel.ORIGINAL_VALUE - - real = Real() - mock = Mock(wraps=real) - mock.method.side_effect = [sentinel.VALUE1, DEFAULT] - - self.assertEqual(mock.method(), sentinel.VALUE1) - self.assertEqual(mock.method(), sentinel.ORIGINAL_VALUE) - self.assertRaises(StopIteration, mock.method) - - - def test_customize_wrapped_object_with_side_effect_iterable(self): - class Real(object): - def method(self): pass - - real = Real() - mock = Mock(wraps=real) - mock.method.side_effect = [sentinel.VALUE1, sentinel.VALUE2] - - self.assertEqual(mock.method(), sentinel.VALUE1) - self.assertEqual(mock.method(), sentinel.VALUE2) - self.assertRaises(StopIteration, mock.method) - - - def test_customize_wrapped_object_with_side_effect_exception(self): - class Real(object): - def method(self): pass - - real = Real() - mock = Mock(wraps=real) - mock.method.side_effect = RuntimeError - - self.assertRaises(RuntimeError, mock.method) - - - def test_customize_wrapped_object_with_side_effect_function(self): - class Real(object): - def method(self): pass - def side_effect(): - return sentinel.VALUE - - real = Real() - mock = Mock(wraps=real) - mock.method.side_effect = side_effect - - self.assertEqual(mock.method(), sentinel.VALUE) - - - def test_customize_wrapped_object_with_return_value(self): - class Real(object): - def method(self): pass - - real = Real() - mock = Mock(wraps=real) - mock.method.return_value = sentinel.VALUE - - self.assertEqual(mock.method(), sentinel.VALUE) - - - def test_customize_wrapped_object_with_return_value_and_side_effect(self): - # side_effect should always take precedence over return_value. - class Real(object): - def method(self): pass - - real = Real() - mock = Mock(wraps=real) - mock.method.side_effect = [sentinel.VALUE1, sentinel.VALUE2] - mock.method.return_value = sentinel.WRONG_VALUE - - self.assertEqual(mock.method(), sentinel.VALUE1) - self.assertEqual(mock.method(), sentinel.VALUE2) - self.assertRaises(StopIteration, mock.method) - - - def test_customize_wrapped_object_with_return_value_and_side_effect2(self): - # side_effect can return DEFAULT to default to return_value - class Real(object): - def method(self): pass - - real = Real() - mock = Mock(wraps=real) - mock.method.side_effect = lambda: DEFAULT - mock.method.return_value = sentinel.VALUE - - self.assertEqual(mock.method(), sentinel.VALUE) - - - def test_customize_wrapped_object_with_return_value_and_side_effect_default(self): - class Real(object): - def method(self): pass - - real = Real() - mock = Mock(wraps=real) - mock.method.side_effect = [sentinel.VALUE1, DEFAULT] - mock.method.return_value = sentinel.RETURN - - self.assertEqual(mock.method(), sentinel.VALUE1) - self.assertEqual(mock.method(), sentinel.RETURN) - self.assertRaises(StopIteration, mock.method) - - def test_exceptional_side_effect(self): mock = Mock(side_effect=AttributeError) self.assertRaises(AttributeError, mock) @@ -738,13 +591,13 @@ class MockTest(unittest.TestCase): def test_assert_called_with_message(self): mock = Mock() - self.assertRaisesRegexp(AssertionError, 'not called', + self.assertRaisesRegex(AssertionError, 'Not called', mock.assert_called_with) def test_assert_called_once_with_message(self): mock = Mock(name='geoffrey') - self.assertRaisesRegexp(AssertionError, + self.assertRaisesRegex(AssertionError, r"Expected 'geoffrey' to be called once\.", mock.assert_called_once_with) @@ -787,30 +640,6 @@ class MockTest(unittest.TestCase): self.assertIsInstance(mock, X) - def test_spec_class_no_object_base(self): - class X: - pass - - mock = Mock(spec=X) - self.assertIsInstance(mock, X) - - if not six.PY2: - # This isn't true on Py2, we should fix if anyone complains: - mock = Mock(spec=X()) - self.assertIsInstance(mock, X) - - self.assertIs(mock.__class__, X) - self.assertEqual(Mock().__class__.__name__, 'Mock') - - mock = Mock(spec_set=X) - self.assertIsInstance(mock, X) - - if not six.PY2: - # This isn't true on Py2, we should fix if anyone complains: - mock = Mock(spec_set=X()) - self.assertIsInstance(mock, X) - - def test_setting_attribute_with_spec_set(self): class X(object): y = 3 @@ -953,15 +782,6 @@ class MockTest(unittest.TestCase): patcher.stop() - def test_dir_does_not_include_deleted_attributes(self): - mock = Mock() - mock.child.return_value = 1 - - self.assertIn('child', dir(mock)) - del mock.child - self.assertNotIn('child', dir(mock)) - - def test_configure_mock(self): mock = Mock(foo='bar') self.assertEqual(mock.foo, 'bar') @@ -985,20 +805,25 @@ class MockTest(unittest.TestCase): def assertRaisesWithMsg(self, exception, message, func, *args, **kwargs): # needed because assertRaisesRegex doesn't work easily with newlines - with self.assertRaises(exception) as context: + try: func(*args, **kwargs) - msg = str(context.exception) + except: + instance = sys.exc_info()[1] + self.assertIsInstance(instance, exception) + else: + self.fail('Exception %r not raised' % (exception,)) + + msg = str(instance) self.assertEqual(msg, message) def test_assert_called_with_failure_message(self): mock = NonCallableMock() - actual = 'not called.' expected = "mock(1, '2', 3, bar='foo')" - message = 'expected call not found.\nExpected: %s\nActual: %s' + message = 'Expected call: %s\nNot called' self.assertRaisesWithMsg( - AssertionError, message % (expected, actual), + AssertionError, message % (expected,), mock.assert_called_with, 1, '2', 3, bar='foo' ) @@ -1011,7 +836,7 @@ class MockTest(unittest.TestCase): for meth in asserters: actual = "foo(1, '2', 3, foo='foo')" expected = "foo(1, '2', 3, bar='foo')" - message = 'expected call not found.\nExpected: %s\nActual: %s' + message = 'Expected call: %s\nActual call: %s' self.assertRaisesWithMsg( AssertionError, message % (expected, actual), meth, 1, '2', 3, bar='foo' @@ -1021,7 +846,7 @@ class MockTest(unittest.TestCase): for meth in asserters: actual = "foo(1, '2', 3, foo='foo')" expected = "foo(bar='foo')" - message = 'expected call not found.\nExpected: %s\nActual: %s' + message = 'Expected call: %s\nActual call: %s' self.assertRaisesWithMsg( AssertionError, message % (expected, actual), meth, bar='foo' @@ -1031,7 +856,7 @@ class MockTest(unittest.TestCase): for meth in asserters: actual = "foo(1, '2', 3, foo='foo')" expected = "foo(1, 2, 3)" - message = 'expected call not found.\nExpected: %s\nActual: %s' + message = 'Expected call: %s\nActual call: %s' self.assertRaisesWithMsg( AssertionError, message % (expected, actual), meth, 1, 2, 3 @@ -1041,7 +866,7 @@ class MockTest(unittest.TestCase): for meth in asserters: actual = "foo(1, '2', 3, foo='foo')" expected = "foo()" - message = 'expected call not found.\nExpected: %s\nActual: %s' + message = 'Expected call: %s\nActual call: %s' self.assertRaisesWithMsg( AssertionError, message % (expected, actual), meth ) @@ -1125,69 +950,6 @@ class MockTest(unittest.TestCase): call().__int__().call_list()) - def test_child_mock_call_equal(self): - m = Mock() - result = m() - result.wibble() - # parent looks like this: - self.assertEqual(m.mock_calls, [call(), call().wibble()]) - # but child should look like this: - self.assertEqual(result.mock_calls, [call.wibble()]) - - - def test_mock_call_not_equal_leaf(self): - m = Mock() - m.foo().something() - self.assertNotEqual(m.mock_calls[1], call.foo().different()) - self.assertEqual(m.mock_calls[0], call.foo()) - - - def test_mock_call_not_equal_non_leaf(self): - m = Mock() - m.foo().bar() - self.assertNotEqual(m.mock_calls[1], call.baz().bar()) - self.assertNotEqual(m.mock_calls[0], call.baz()) - - - def test_mock_call_not_equal_non_leaf_params_different(self): - m = Mock() - m.foo(x=1).bar() - # This isn't ideal, but there's no way to fix it without breaking backwards compatibility: - self.assertEqual(m.mock_calls[1], call.foo(x=2).bar()) - - - def test_mock_call_not_equal_non_leaf_attr(self): - m = Mock() - m.foo.bar() - self.assertNotEqual(m.mock_calls[0], call.baz.bar()) - - - def test_mock_call_not_equal_non_leaf_call_versus_attr(self): - m = Mock() - m.foo.bar() - self.assertNotEqual(m.mock_calls[0], call.foo().bar()) - - - def test_mock_call_repr(self): - m = Mock() - m.foo().bar().baz.bob() - self.assertEqual(repr(m.mock_calls[0]), 'call.foo()') - self.assertEqual(repr(m.mock_calls[1]), 'call.foo().bar()') - self.assertEqual(repr(m.mock_calls[2]), 'call.foo().bar().baz.bob()') - - - def test_mock_call_repr_loop(self): - m = Mock() - m.foo = m - repr(m.foo()) - self.assertRegexpMatches(repr(m.foo()), r"<Mock name='mock\(\)' id='\d+'>") - - - def test_mock_calls_contains(self): - m = Mock() - self.assertFalse([call()] in m.mock_calls) - - def test_subclassing(self): class Subclass(Mock): pass @@ -1246,8 +1008,9 @@ class MockTest(unittest.TestCase): mock(2, b=4) self.assertEqual(len(mock.call_args), 2) - self.assertEqual(mock.call_args.args, (2,)) - self.assertEqual(mock.call_args.kwargs, dict(b=4)) + args, kwargs = mock.call_args + self.assertEqual(args, (2,)) + self.assertEqual(kwargs, dict(b=4)) expected_list = [((1,), dict(a=3)), ((2,), dict(b=4))] for expected, call_args in zip(expected_list, mock.call_args_list): @@ -1402,7 +1165,8 @@ class MockTest(unittest.TestCase): def test_assert_has_calls_with_function_spec(self): - def f(a, b, c, d=None): pass + def f(a, b, c, d=None): + pass mock = Mock(spec=f) @@ -1460,7 +1224,8 @@ class MockTest(unittest.TestCase): def test_assert_any_call_with_function_spec(self): - def f(a, b, c, d=None): pass + def f(a, b, c, d=None): + pass mock = Mock(spec=f) @@ -1480,7 +1245,8 @@ class MockTest(unittest.TestCase): def test_mock_calls_create_autospec(self): - def f(a, b): pass + def f(a, b): + pass obj = Iter() obj.f = f @@ -1501,20 +1267,6 @@ class MockTest(unittest.TestCase): m = mock.create_autospec(object(), name='sweet_func') self.assertIn('sweet_func', repr(m)) - #Issue23078 - def test_create_autospec_classmethod_and_staticmethod(self): - class TestClass: - @classmethod - def class_method(cls): pass - - @staticmethod - def static_method(): pass - for method in ('class_method', 'static_method'): - mock_method = mock.create_autospec(getattr(TestClass, method)) - mock_method() - mock_method.assert_called_once_with() - self.assertRaises(TypeError, mock_method, 'extra_arg') - #Issue21238 def test_mock_unsafe(self): m = Mock() @@ -1534,13 +1286,6 @@ class MockTest(unittest.TestCase): with self.assertRaises(AssertionError): m.hello.assert_not_called() - def test_assert_not_called_message(self): - m = Mock() - m(1, 2) - self.assertRaisesRegexp(AssertionError, - re.escape("Calls: [call(1, 2)]"), - m.assert_not_called) - def test_assert_called(self): m = Mock() with self.assertRaises(AssertionError): @@ -1562,20 +1307,6 @@ class MockTest(unittest.TestCase): with self.assertRaises(AssertionError): m.hello.assert_called_once() - def test_assert_called_once_message(self): - m = Mock() - m(1, 2) - m(3) - self.assertRaisesRegexp(AssertionError, - re.escape("Calls: [call(1, 2), call(3)]"), - m.assert_called_once) - - def test_assert_called_once_message_not_called(self): - m = Mock() - with self.assertRaises(AssertionError) as e: - m.assert_called_once() - self.assertNotIn("Calls:", str(e.exception)) - #Issue21256 printout of keyword args should be in deterministic order def test_sorted_call_signature(self): m = Mock() @@ -1593,24 +1324,6 @@ class MockTest(unittest.TestCase): self.assertEqual(m.method_calls[0], c) self.assertEqual(m.method_calls[1], i) - def test_reset_return_sideeffect(self): - m = Mock(return_value=10, side_effect=[2,3]) - m.reset_mock(return_value=True, side_effect=True) - self.assertIsInstance(m.return_value, Mock) - self.assertEqual(m.side_effect, None) - - def test_reset_return(self): - m = Mock(return_value=10, side_effect=[2,3]) - m.reset_mock(return_value=True) - self.assertIsInstance(m.return_value, Mock) - self.assertNotEqual(m.side_effect, None) - - def test_reset_sideeffect(self): - m = Mock(return_value=10, side_effect=[2,3]) - m.reset_mock(side_effect=True) - self.assertEqual(m.return_value, 10) - self.assertEqual(m.side_effect, None) - def test_mock_add_spec(self): class _One(object): one = 1 @@ -1755,16 +1468,6 @@ class MockTest(unittest.TestCase): f2_data = f2.read() self.assertEqual(f1_data, f2_data) - def test_mock_open_dunder_iter_issue(self): - # Test dunder_iter method generates the expected result and - # consumes the iterator. - mocked_open = mock.mock_open(read_data='Remarkable\nNorwegian Blue') - f1 = mocked_open('a-name') - lines = [line for line in f1] - self.assertEqual(lines[0], 'Remarkable\n') - self.assertEqual(lines[1], 'Norwegian Blue') - self.assertEqual(list(f1), []) - def test_mock_open_write(self): # Test exception in file writing write() mock_namedtemp = mock.mock_open(mock.MagicMock(name='JLV')) @@ -1783,21 +1486,7 @@ class MockTest(unittest.TestCase): second = mopen().readline() self.assertEqual('abc', first) self.assertEqual('abc', second) - - - def test_mock_open_after_eof(self): - # read, readline and readlines should work after end of file. - _open = mock.mock_open(read_data='foo') - h = _open('bar') - h.read() - self.assertEqual('', h.read()) - self.assertEqual('', h.read()) - self.assertEqual('', h.readline()) - self.assertEqual('', h.readline()) - self.assertEqual([], h.readlines()) - self.assertEqual([], h.readlines()) - - + def test_mock_parents(self): for Klass in Mock, MagicMock: m = Klass() @@ -1873,43 +1562,6 @@ class MockTest(unittest.TestCase): self.assertRaises(AttributeError, getattr, mock, 'f') - def test_mock_does_not_raise_on_repeated_attribute_deletion(self): - # bpo-20239: Assigning and deleting twice an attribute raises. - for mock in (Mock(), MagicMock(), NonCallableMagicMock(), - NonCallableMock()): - mock.foo = 3 - self.assertTrue(hasattr(mock, 'foo')) - self.assertEqual(mock.foo, 3) - - del mock.foo - self.assertFalse(hasattr(mock, 'foo')) - - mock.foo = 4 - self.assertTrue(hasattr(mock, 'foo')) - self.assertEqual(mock.foo, 4) - - del mock.foo - self.assertFalse(hasattr(mock, 'foo')) - - - def test_mock_raises_when_deleting_nonexistent_attribute(self): - for mock in (Mock(), MagicMock(), NonCallableMagicMock(), - NonCallableMock()): - del mock.foo - with self.assertRaises(AttributeError): - del mock.foo - - - def test_reset_mock_does_not_raise_on_attr_deletion(self): - # bpo-31177: reset_mock should not raise AttributeError when attributes - # were deleted in a mock instance - mock = Mock() - mock.child = True - del mock.child - mock.reset_mock() - self.assertFalse(hasattr(mock, 'child')) - - def test_class_assignable(self): for mock in Mock(), MagicMock(): self.assertNotIsInstance(mock, int) @@ -1918,59 +1570,21 @@ class MockTest(unittest.TestCase): self.assertIsInstance(mock, int) mock.foo - def test_name_attribute_of_call(self): - # bpo-35357: _Call should not disclose any attributes whose names - # may clash with popular ones (such as ".name") - self.assertIsNotNone(call.name) - self.assertEqual(type(call.name), _Call) - self.assertEqual(type(call.name().name), _Call) - def test_parent_attribute_of_call(self): - # bpo-35357: _Call should not disclose any attributes whose names - # may clash with popular ones (such as ".parent") - self.assertIsNotNone(call.parent) - self.assertEqual(type(call.parent), _Call) - self.assertEqual(type(call.parent().parent), _Call) - - def test_parent_propagation_with_create_autospec(self): - def foo(a, b): pass - - mock = Mock() - mock.child = create_autospec(foo) - mock.child(1, 2) - self.assertRaises(TypeError, mock.child, 1) - self.assertEqual(mock.mock_calls, [call.child(1, 2)]) - - def test_isinstance_under_settrace(self): - # bpo-36593 : __class__ is not set for a class that has __class__ - # property defined when it's used with sys.settrace(trace) set. - # Delete the module to force reimport with tracing function set - # restore the old reference later since there are other tests that are - # dependent on unittest.mock.patch. In testpatch.PatchTest - # test_patch_dict_test_prefix and test_patch_test_prefix not restoring - # causes the objects patched to go out of sync - old_patch = mock.patch - # Directly using __setattr__ on unittest.mock causes current imported - # reference to be updated. Use a lambda so that during cleanup the - # re-imported new reference is updated. - self.addCleanup(lambda patch: setattr(mock, 'patch', patch), - old_patch) - with patch.dict('sys.modules'): - del sys.modules['mock.mock'] - # This trace will stop coverage being measured ;-) - def trace(frame, event, arg): # pragma: no cover - return trace - self.addCleanup(sys.settrace, sys.gettrace()) - sys.settrace(trace) - from mock.mock import ( - Mock, MagicMock, NonCallableMock, NonCallableMagicMock - ) - mocks = [ - Mock, MagicMock, NonCallableMock, NonCallableMagicMock - ] - for mock_ in mocks: - obj = mock_(spec=Something) - self.assertIsInstance(obj, Something) + @unittest.expectedFailure + def test_pickle(self): + for Klass in (MagicMock, Mock, Subclass, NonCallableMagicMock): + mock = Klass(name='foo', attribute=3) + mock.foo(1, 2, 3) + data = pickle.dumps(mock) + new = pickle.loads(data) + + new.foo.assert_called_once_with(1, 2, 3) + self.assertFalse(new.called) + self.assertTrue(is_instance(new, Klass)) + self.assertIsInstance(new, Thing) + self.assertIn('name="foo"', repr(new)) + self.assertEqual(new.attribute, 3) if __name__ == '__main__': diff --git a/mock/tests/testpatch.py b/mock/tests/testpatch.py index bbd6d26..f31ccef 100644 --- a/mock/tests/testpatch.py +++ b/mock/tests/testpatch.py @@ -6,14 +6,14 @@ import os import sys import six -import unittest +import unittest2 as unittest from mock.tests import support -from mock.tests.support import SomeClass, is_instance, uncache +from mock.tests.support import SomeClass, is_instance from mock import ( NonCallableMock, CallableMixin, patch, sentinel, - MagicMock, Mock, NonCallableMagicMock, + MagicMock, Mock, NonCallableMagicMock, patch, DEFAULT, call ) from mock.mock import _patch, _get_target @@ -47,24 +47,23 @@ something_else = sentinel.SomethingElse class Foo(object): - def __init__(self, a): pass - def f(self, a): pass - def g(self): pass + def __init__(self, a): + pass + def f(self, a): + pass + def g(self): + pass foo = 'bar' - @staticmethod - def static_method(): pass - - @classmethod - def class_method(cls): pass - class Bar(object): - def a(self): pass + def a(self): + pass foo_name = '%s.Foo' % __name__ -def function(a, b=Foo): pass +def function(a, b=Foo): + pass class Container(object): @@ -367,19 +366,31 @@ class PatchTest(unittest.TestCase): def test_patch_wont_create_by_default(self): - with self.assertRaises(AttributeError): + try: @patch('%s.frooble' % builtin_string, sentinel.Frooble) - def test(): pass + def test(): + self.assertEqual(frooble, sentinel.Frooble) test() + except AttributeError: + pass + else: + self.fail('Patching non existent attributes should fail') + self.assertRaises(NameError, lambda: frooble) def test_patchobject_wont_create_by_default(self): - with self.assertRaises(AttributeError): + try: @patch.object(SomeClass, 'ord', sentinel.Frooble) - def test(): pass + def test(): + self.fail('Patching non existent attributes should fail') + test() + except AttributeError: + pass + else: + self.fail('Patching non existent attributes should fail') self.assertFalse(hasattr(SomeClass, 'ord')) @@ -469,9 +480,6 @@ class PatchTest(unittest.TestCase): attribute = sentinel.Original class Foo(object): - - test_class_attr = 'whatever' - def test_method(other_self, mock_something): self.assertEqual(PTModule.something, mock_something, "unpatched") @@ -630,7 +638,8 @@ class PatchTest(unittest.TestCase): @patch('%s.SomeClass' % __name__, object(), autospec=True) @patch.object(SomeClass, object()) @patch.dict(foo) - def some_name(): pass + def some_name(): + pass self.assertEqual(some_name.__name__, 'some_name') @@ -641,9 +650,12 @@ class PatchTest(unittest.TestCase): @patch.dict(foo, {'a': 'b'}) def test(): raise NameError('Konrad') - - with self.assertRaises(NameError): + try: test() + except NameError: + pass + else: + self.fail('NameError not raised by test') self.assertEqual(foo, {}) @@ -656,37 +668,57 @@ class PatchTest(unittest.TestCase): test() - def test_patch_dict_with_unicode(self): - @patch.dict(u'os.environ', {'konrad_delong': 'some value'}) - def test(): - self.assertIn('konrad_delong', os.environ) + @unittest.expectedFailure + def test_patch_descriptor(self): + # would be some effort to fix this - we could special case the + # builtin descriptors: classmethod, property, staticmethod + class Nothing(object): + foo = None - test() + class Something(object): + foo = {} + @patch.object(Nothing, 'foo', 2) + @classmethod + def klass(cls): + self.assertIs(cls, Something) - def test_patch_dict_decorator_resolution(self): - # bpo-35512: Ensure that patch with a string target resolves to - # the new dictionary during function call - original = support.target.copy() - @patch.dict('mock.tests.support.target', {'bar': 'BAR'}) - def test(): - self.assertEqual(support.target, {'foo': 'BAZ', 'bar': 'BAR'}) - try: - support.target = {'foo': 'BAZ'} - test() - self.assertEqual(support.target, {'foo': 'BAZ'}) - finally: - support.target = original + @patch.object(Nothing, 'foo', 2) + @staticmethod + def static(arg): + return arg + + @patch.dict(foo) + @classmethod + def klass_dict(cls): + self.assertIs(cls, Something) + + @patch.dict(foo) + @staticmethod + def static_dict(arg): + return arg + + # these will raise exceptions if patching descriptors is broken + self.assertEqual(Something.static('f00'), 'f00') + Something.klass() + self.assertEqual(Something.static_dict('f00'), 'f00') + Something.klass_dict() + + something = Something() + self.assertEqual(something.static('f00'), 'f00') + something.klass() + self.assertEqual(something.static_dict('f00'), 'f00') + something.klass_dict() def test_patch_spec_set(self): - @patch('%s.SomeClass' % __name__, spec=SomeClass, spec_set=True) + @patch('%s.SomeClass' % __name__, spec_set=SomeClass) def test(MockClass): MockClass.z = 'foo' self.assertRaises(AttributeError, test) - @patch.object(support, 'SomeClass', spec=SomeClass, spec_set=True) + @patch.object(support, 'SomeClass', spec_set=SomeClass) def test(MockClass): MockClass.z = 'foo' @@ -727,18 +759,10 @@ class PatchTest(unittest.TestCase): def test_stop_without_start(self): - # bpo-36366: calling stop without start will return None. patcher = patch(foo_name, 'bar', 3) - self.assertIsNone(patcher.stop()) - - def test_stop_idempotent(self): - # bpo-36366: calling stop on an already stopped patch will return None. - patcher = patch(foo_name, 'bar', 3) - - patcher.start() - patcher.stop() - self.assertIsNone(patcher.stop()) + # calling stop without start used to produce a very obscure error + self.assertRaises(RuntimeError, patcher.stop) def test_patchobject_start_stop(self): @@ -878,13 +902,17 @@ class PatchTest(unittest.TestCase): def test_autospec(self): class Boo(object): - def __init__(self, a): pass - def f(self, a): pass - def g(self): pass + def __init__(self, a): + pass + def f(self, a): + pass + def g(self): + pass foo = 'bar' class Bar(object): - def a(self): pass + def a(self): + pass def _test(mock): mock(1) @@ -946,14 +974,8 @@ class PatchTest(unittest.TestCase): def test_autospec_function(self): @patch('%s.function' % __name__, autospec=True) def test(mock): - function.assert_not_called() - self.assertRaises(AssertionError, function.assert_called) - self.assertRaises(AssertionError, function.assert_called_once) function(1) - self.assertRaises(AssertionError, function.assert_not_called) function.assert_called_with(1) - function.assert_called() - function.assert_called_once() function(2, 3) function.assert_called_with(2, 3) @@ -974,18 +996,6 @@ class PatchTest(unittest.TestCase): self.assertEqual(result, 3) - def test_autospec_staticmethod(self): - with patch('%s.Foo.static_method' % __name__, autospec=True) as method: - Foo.static_method() - method.assert_called_once_with() - - - def test_autospec_classmethod(self): - with patch('%s.Foo.class_method' % __name__, autospec=True) as method: - Foo.class_method() - method.assert_called_once_with() - - def test_autospec_with_new(self): patcher = patch('%s.function' % __name__, new=3, autospec=True) self.assertRaises(TypeError, patcher.start) @@ -1255,6 +1265,7 @@ class PatchTest(unittest.TestCase): def test_patch_multiple_create_mocks_different_order(self): + # bug revealed by Jython! original_f = Foo.f original_g = Foo.g @@ -1431,17 +1442,20 @@ class PatchTest(unittest.TestCase): @patch.object(Foo, 'g', 1) @patch.object(Foo, 'missing', 1) @patch.object(Foo, 'f', 1) - def thing1(): pass + def thing1(): + pass @patch.object(Foo, 'missing', 1) @patch.object(Foo, 'g', 1) @patch.object(Foo, 'f', 1) - def thing2(): pass + def thing2(): + pass @patch.object(Foo, 'g', 1) @patch.object(Foo, 'f', 1) @patch.object(Foo, 'missing', 1) - def thing3(): pass + def thing3(): + pass for func in thing1, thing2, thing3: self.assertRaises(AttributeError, func) @@ -1460,17 +1474,20 @@ class PatchTest(unittest.TestCase): @patch.object(Foo, 'g', 1) @patch.object(Foo, 'foo', new_callable=crasher) @patch.object(Foo, 'f', 1) - def thing1(): pass + def thing1(): + pass @patch.object(Foo, 'foo', new_callable=crasher) @patch.object(Foo, 'g', 1) @patch.object(Foo, 'f', 1) - def thing2(): pass + def thing2(): + pass @patch.object(Foo, 'g', 1) @patch.object(Foo, 'f', 1) @patch.object(Foo, 'foo', new_callable=crasher) - def thing3(): pass + def thing3(): + pass for func in thing1, thing2, thing3: self.assertRaises(NameError, func) @@ -1496,7 +1513,8 @@ class PatchTest(unittest.TestCase): patcher.additional_patchers = additionals @patcher - def func(): pass + def func(): + pass self.assertRaises(AttributeError, func) self.assertEqual(Foo.f, original_f) @@ -1524,7 +1542,8 @@ class PatchTest(unittest.TestCase): patcher.additional_patchers = additionals @patcher - def func(): pass + def func(): + pass self.assertRaises(NameError, func) self.assertEqual(Foo.f, original_f) @@ -1644,20 +1663,21 @@ class PatchTest(unittest.TestCase): def test_patch_imports_lazily(self): + sys.modules.pop('squizz', None) + p1 = patch('squizz.squozz') self.assertRaises(ImportError, p1.start) - with uncache('squizz'): - squizz = Mock() - sys.modules['squizz'] = squizz - - squizz.squozz = 6 - p1 = patch('squizz.squozz') - squizz.squozz = 3 - p1.start() - p1.stop() + squizz = Mock() + squizz.squozz = 6 + sys.modules['squizz'] = squizz + p1 = patch('squizz.squozz') + squizz.squozz = 3 + p1.start() + p1.stop() self.assertEqual(squizz.squozz, 3) + def test_patch_propogrates_exc_on_exit(self): class holder: exc_info = None, None, None @@ -1679,12 +1699,7 @@ class PatchTest(unittest.TestCase): def test(mock): raise RuntimeError - with uncache('squizz'): - squizz = Mock() - sys.modules['squizz'] = squizz - - self.assertRaises(RuntimeError, test) - + self.assertRaises(RuntimeError, test) self.assertIs(holder.exc_info[0], RuntimeError) self.assertIsNotNone(holder.exc_info[1], 'exception value not propgated') @@ -1864,35 +1879,5 @@ class PatchTest(unittest.TestCase): self.assertEqual(foo(), 1) self.assertEqual(foo(), 0) - - def test_dotted_but_module_not_loaded(self): - # This exercises the AttributeError branch of _dot_lookup. - # make sure it's there - import mock.tests.support - # now make sure it's not: - with patch.dict('sys.modules'): - del sys.modules['mock.tests.support'] - del sys.modules['mock.tests'] - del sys.modules['mock.mock'] - del sys.modules['mock'] - # now make sure we can patch based on a dotted path: - @patch('mock.tests.support.X') - def test(mock): - pass - test() - - - def test_invalid_target(self): - with self.assertRaises(TypeError): - patch('') - - - def test_cant_set_kwargs_when_passing_a_mock(self): - @patch('mock.tests.support.X', new=object(), x=1) - def test(): pass - with self.assertRaises(TypeError): - test() - - if __name__ == '__main__': unittest.main() diff --git a/mock/tests/testsealable.py b/mock/tests/testsealable.py deleted file mode 100644 index 63a8541..0000000 --- a/mock/tests/testsealable.py +++ /dev/null @@ -1,176 +0,0 @@ -import unittest -import mock - - -class SampleObject: - - def method_sample1(self): pass - - def method_sample2(self): pass - - -class TestSealable(unittest.TestCase): - - def test_attributes_return_more_mocks_by_default(self): - m = mock.Mock() - - self.assertIsInstance(m.test, mock.Mock) - self.assertIsInstance(m.test(), mock.Mock) - self.assertIsInstance(m.test().test2(), mock.Mock) - - def test_new_attributes_cannot_be_accessed_on_seal(self): - m = mock.Mock() - - mock.seal(m) - with self.assertRaises(AttributeError): - m.test - with self.assertRaises(AttributeError): - m() - - def test_new_attributes_cannot_be_set_on_seal(self): - m = mock.Mock() - - mock.seal(m) - with self.assertRaises(AttributeError): - m.test = 1 - - def test_existing_attributes_can_be_set_on_seal(self): - m = mock.Mock() - m.test.test2 = 1 - - mock.seal(m) - m.test.test2 = 2 - self.assertEqual(m.test.test2, 2) - - def test_new_attributes_cannot_be_set_on_child_of_seal(self): - m = mock.Mock() - m.test.test2 = 1 - - mock.seal(m) - with self.assertRaises(AttributeError): - m.test.test3 = 1 - - def test_existing_attributes_allowed_after_seal(self): - m = mock.Mock() - - m.test.return_value = 3 - - mock.seal(m) - self.assertEqual(m.test(), 3) - - def test_initialized_attributes_allowed_after_seal(self): - m = mock.Mock(test_value=1) - - mock.seal(m) - self.assertEqual(m.test_value, 1) - - def test_call_on_sealed_mock_fails(self): - m = mock.Mock() - - mock.seal(m) - with self.assertRaises(AttributeError): - m() - - def test_call_on_defined_sealed_mock_succeeds(self): - m = mock.Mock(return_value=5) - - mock.seal(m) - self.assertEqual(m(), 5) - - def test_seals_recurse_on_added_attributes(self): - m = mock.Mock() - - m.test1.test2().test3 = 4 - - mock.seal(m) - self.assertEqual(m.test1.test2().test3, 4) - with self.assertRaises(AttributeError): - m.test1.test2().test4 - with self.assertRaises(AttributeError): - m.test1.test3 - - def test_seals_recurse_on_magic_methods(self): - m = mock.MagicMock() - - m.test1.test2["a"].test3 = 4 - m.test1.test3[2:5].test3 = 4 - - mock.seal(m) - self.assertEqual(m.test1.test2["a"].test3, 4) - self.assertEqual(m.test1.test2[2:5].test3, 4) - with self.assertRaises(AttributeError): - m.test1.test2["a"].test4 - with self.assertRaises(AttributeError): - m.test1.test3[2:5].test4 - - def test_seals_dont_recurse_on_manual_attributes(self): - m = mock.Mock(name="root_mock") - - m.test1.test2 = mock.Mock(name="not_sealed") - m.test1.test2.test3 = 4 - - mock.seal(m) - self.assertEqual(m.test1.test2.test3, 4) - m.test1.test2.test4 # Does not raise - m.test1.test2.test4 = 1 # Does not raise - - def test_integration_with_spec_att_definition(self): - """You are not restricted when using mock with spec""" - m = mock.Mock(SampleObject) - - m.attr_sample1 = 1 - m.attr_sample3 = 3 - - mock.seal(m) - self.assertEqual(m.attr_sample1, 1) - self.assertEqual(m.attr_sample3, 3) - with self.assertRaises(AttributeError): - m.attr_sample2 - - def test_integration_with_spec_method_definition(self): - """You need to defin the methods, even if they are in the spec""" - m = mock.Mock(SampleObject) - - m.method_sample1.return_value = 1 - - mock.seal(m) - self.assertEqual(m.method_sample1(), 1) - with self.assertRaises(AttributeError): - m.method_sample2() - - def test_integration_with_spec_method_definition_respects_spec(self): - """You cannot define methods out of the spec""" - m = mock.Mock(SampleObject) - - with self.assertRaises(AttributeError): - m.method_sample3.return_value = 3 - - def test_sealed_exception_has_attribute_name(self): - m = mock.Mock() - - mock.seal(m) - with self.assertRaises(AttributeError) as cm: - m.SECRETE_name - self.assertIn("SECRETE_name", str(cm.exception)) - - def test_attribute_chain_is_maintained(self): - m = mock.Mock(name="mock_name") - m.test1.test2.test3.test4 - - mock.seal(m) - with self.assertRaises(AttributeError) as cm: - m.test1.test2.test3.test4.boom - self.assertIn("mock_name.test1.test2.test3.test4.boom", str(cm.exception)) - - def test_call_chain_is_maintained(self): - m = mock.Mock() - m.test1().test2.test3().test4 - - mock.seal(m) - with self.assertRaises(AttributeError) as cm: - m.test1().test2.test3().test4() - self.assertIn("mock.test1().test2.test3().test4", str(cm.exception)) - - -if __name__ == "__main__": - unittest.main() diff --git a/mock/tests/testsentinel.py b/mock/tests/testsentinel.py index 1411445..69b2042 100644 --- a/mock/tests/testsentinel.py +++ b/mock/tests/testsentinel.py @@ -2,9 +2,8 @@ # E-mail: fuzzyman AT voidspace DOT org DOT uk # http://www.voidspace.org.uk/python/mock/ -import unittest -import copy -import pickle +import unittest2 as unittest + from mock import sentinel, DEFAULT @@ -29,16 +28,6 @@ class SentinelTest(unittest.TestCase): # If this doesn't raise an AttributeError then help(mock) is broken self.assertRaises(AttributeError, lambda: sentinel.__bases__) - def testPickle(self): - for proto in range(pickle.HIGHEST_PROTOCOL+1): - pickled = pickle.dumps(sentinel.whatever, proto) - unpickled = pickle.loads(pickled) - self.assertIs(unpickled, sentinel.whatever) - - def testCopy(self): - self.assertIs(copy.copy(sentinel.whatever), sentinel.whatever) - self.assertIs(copy.deepcopy(sentinel.whatever), sentinel.whatever) - if __name__ == '__main__': unittest.main() diff --git a/mock/tests/testsupport.py b/mock/tests/testsupport.py deleted file mode 100644 index 4882572..0000000 --- a/mock/tests/testsupport.py +++ /dev/null @@ -1,14 +0,0 @@ -# Tests to make sure helpers we backport are actually working! -from unittest import TestCase - -from .support import uncache - - -class TestUncache(TestCase): - - def test_cant_uncache_sys(self): - with self.assertRaises(ValueError): - with uncache('sys'): pass - - def test_uncache_non_existent(self): - with uncache('mock.tests.support.bad'): pass diff --git a/mock/tests/testwith.py b/mock/tests/testwith.py index 587fde9..aa7812b 100644 --- a/mock/tests/testwith.py +++ b/mock/tests/testwith.py @@ -4,7 +4,7 @@ from warnings import catch_warnings -import unittest +import unittest2 as unittest from mock.tests.support import is_instance from mock import MagicMock, Mock, patch, sentinel, mock_open, call @@ -14,8 +14,6 @@ something = sentinel.Something something_else = sentinel.SomethingElse -class SampleException(Exception): pass - class WithTest(unittest.TestCase): @@ -26,10 +24,14 @@ class WithTest(unittest.TestCase): def test_with_statement_exception(self): - with self.assertRaises(SampleException): + try: with patch('%s.something' % __name__, sentinel.Something2): self.assertEqual(something, sentinel.Something2, "unpatched") - raise SampleException() + raise Exception('pow') + except Exception: + pass + else: + self.fail("patch swallowed exception") self.assertEqual(something, sentinel.Something) @@ -129,19 +131,6 @@ class WithTest(unittest.TestCase): self.assertEqual(foo, {}) - def test_double_patch_instance_method(self): - class C: - def f(self): pass - - c = C() - - with patch.object(c, 'f', autospec=True) as patch1: - with patch.object(c, 'f', autospec=True) as patch2: - c.f() - self.assertEqual(patch2.call_count, 1) - self.assertEqual(patch1.call_count, 0) - c.f() - self.assertEqual(patch1.call_count, 1) class TestMockOpen(unittest.TestCase): @@ -204,7 +193,6 @@ class TestMockOpen(unittest.TestCase): def test_readline_data(self): # Check that readline will return all the lines from the fake file - # And that once fully consumed, readline will return an empty string. mock = mock_open(read_data='foo\nbar\nbaz\n') with patch('%s.open' % __name__, mock, create=True): h = open('bar') @@ -214,7 +202,6 @@ class TestMockOpen(unittest.TestCase): self.assertEqual(line1, 'foo\n') self.assertEqual(line2, 'bar\n') self.assertEqual(line3, 'baz\n') - self.assertEqual(h.readline(), '') # Check that we properly emulate a file that doesn't end in a newline mock = mock_open(read_data='foo') @@ -222,19 +209,6 @@ class TestMockOpen(unittest.TestCase): h = open('bar') result = h.readline() self.assertEqual(result, 'foo') - self.assertEqual(h.readline(), '') - - - def test_dunder_iter_data(self): - # Check that dunder_iter will return all the lines from the fake file. - mock = mock_open(read_data='foo\nbar\nbaz\n') - with patch('%s.open' % __name__, mock, create=True): - h = open('bar') - lines = [l for l in h] - self.assertEqual(lines[0], 'foo\n') - self.assertEqual(lines[1], 'bar\n') - self.assertEqual(lines[2], 'baz\n') - self.assertEqual(h.readline(), '') def test_readlines_data(self): @@ -288,12 +262,7 @@ class TestMockOpen(unittest.TestCase): # for mocks returned by mock_open some_data = 'foo\nbar\nbaz' mock = mock_open(read_data=some_data) - self.assertEqual(mock().read(10), some_data[:10]) - self.assertEqual(mock().read(10), some_data[:10]) - - f = mock() - self.assertEqual(f.read(10), some_data[:10]) - self.assertEqual(f.read(10), some_data[10:]) + self.assertEqual(mock().read(10), some_data) def test_interleaved_reads(self): diff --git a/release.py b/release.py deleted file mode 100644 index 2556d50..0000000 --- a/release.py +++ /dev/null @@ -1,92 +0,0 @@ -import re -from glob import glob -from os.path import join -from subprocess import call - -import blurb as blurb_module -from argparse import ArgumentParser -from mock import version_info - -VERSION_TYPES = ['major', 'minor', 'bugfix'] - - -def incremented_version(version_info, type_): - type_index = VERSION_TYPES.index(type_) - version_info = tuple(e+(1 if i==type_index else 0) - for i, e in enumerate(version_info)) - return '.'.join(str(p) for p in version_info) - - -def text_from_news(): - # hack: - blurb_module.sections.append('NEWS.d') - - blurbs = blurb_module.Blurbs() - for path in glob(join('NEWS.d', '*')): - blurbs.load_next(path) - - text = [] - for metadata, body in blurbs: - bpo = metadata['bpo'] - body = f"- Issue #{bpo}: " + body - text.append(blurb_module.textwrap_body(body, subsequent_indent=' ')) - - return '\n'.join(text) - - -def news_to_changelog(version): - with open('CHANGELOG.rst') as source: - current_changelog = source.read() - - text = [version] - text.append('-'*len(version)) - text.append('') - text.append(text_from_news()) - text.append(current_changelog) - - new_changelog = '\n'.join(text) - with open('CHANGELOG.rst', 'w') as target: - target.write(new_changelog) - - -def update_version(new_version): - path = join('mock', 'mock.py') - with open(path) as source: - text = source.read() - - text = re.sub("(__version__ = ')[^']+(')", - r"\g<1>"+new_version+r"\2", - text) - - with open(path, 'w') as target: - target.write(text) - - -def git(command): - return call('git '+command, shell=True) - - -def git_commit(new_version): - git('rm NEWS.d/*') - git('add CHANGELOG.rst') - git('add mock/mock.py') - git(f'commit -m "Preparing for {new_version} release."') - - -def parse_args(): - parser = ArgumentParser() - parser.add_argument('type', choices=VERSION_TYPES) - return parser.parse_args() - - -def main(): - args = parse_args() - new_version = incremented_version(version_info, args.type) - news_to_changelog(new_version) - update_version(new_version) - git_commit(new_version) - print(f'{new_version} ready to push, please check the HEAD commit first!') - - -if __name__ == '__main__': - main() diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..31bbe5d --- /dev/null +++ b/requirements.txt @@ -0,0 +1,6 @@ +funcsigs>=1;python_version<"3.3" +# For runtime needs this is correct. For setup_requires needs, 1.2.0 is needed +# but setuptools can't cope with conflicts in setup_requires, so thats +# unversioned. +pbr>=0.11 +six>=1.9 @@ -1,12 +1,11 @@ [metadata] name = mock summary = Rolling backport of unittest.mock for all Pythons -home-page = http://mock.readthedocs.org/en/latest/ +home-page = https://github.com/testing-cabal/mock description-file = README.rst author = Testing Cabal author-email = testing-in-python@lists.idyll.org -license = OSI Approved :: BSD License -classifier = +classifier = Development Status :: 5 - Production/Stable Environment :: Console Intended Audience :: Developers @@ -16,11 +15,12 @@ classifier = Programming Language :: Python :: 2 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 - Programming Language :: Python :: 3.7 Programming Language :: Python :: Implementation :: CPython + Programming Language :: Python :: Implementation :: Jython Programming Language :: Python :: Implementation :: PyPy Topic :: Software Development :: Libraries Topic :: Software Development :: Libraries :: Python Modules @@ -28,28 +28,14 @@ classifier = keyword = testing, test, mock, mocking, unittest, patching, stubs, fakes, doubles -[options] -install_requires = - six - funcsigs>=1;python_version<"3.3" -python_requires=>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* -packages = mock - -[options.extras_require] +[extras] +test = + unittest2>=1.1.0 docs = sphinx -test = - pytest - pytest-cov -build = - twine - wheel - blurb + +[files] +packages = mock [bdist_wheel] universal = 1 - -[tool:pytest] -python_files=test*.py -filterwarnings = - ignore::DeprecationWarning @@ -1,10 +1,6 @@ -import re -from os.path import join - +#!/usr/bin/env python import setuptools setuptools.setup( - version=re.search("__version__ = '([^']+)'", - open(join('mock', 'mock.py')).read()).group(1), - long_description=open('README.rst').read(), -) + setup_requires=['pbr>=1.3', 'setuptools>=17.1'], + pbr=True) diff --git a/tools/applypatch-transform b/tools/applypatch-transform new file mode 100755 index 0000000..52fcd93 --- /dev/null +++ b/tools/applypatch-transform @@ -0,0 +1,38 @@ +#!/bin/sh +# +# An example hook script to transform a patch taken from an email +# by git am. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. The hook is +# allowed to edit the patch file. +# +# To enable this hook, rename this file to "applypatch-transform". +# +# This example changes the path of Lib/unittest/mock.py to mock.py +# Lib/unittest/tests/testmock to tests and Misc/NEWS to NEWS, and +# finally skips any patches that did not alter mock.py or its tests. + +set -eux + +patch_path=$1 + +# Pull out mock.py +filterdiff --clean --strip 3 --addprefix=a/mock/ -i 'a/Lib/unittest/mock.py' -i 'b/Lib/unittest/mock.py' $patch_path > $patch_path.mock +# And the tests +filterdiff --clean --strip 5 --addprefix=a/mock/tests/ -i 'a/Lib/unittest/test/testmock/*.py' -i 'b/Lib/unittest/test/testmock/*.py' $patch_path > $patch_path.tests +# Lastly we want to pick up any NEWS entries. +filterdiff --strip 2 --addprefix=a/ -i a/Misc/NEWS -i b/Misc/NEWS $patch_path > $patch_path.NEWS +cp $patch_path $patch_path.orig +# bash +cat $patch_path.mock $patch_path.tests > $patch_path +filtered=$(cat $patch_path) +if [ -n "${filtered}" ]; then + cat $patch_path.NEWS >> $patch_path + exitcode=0 +else + exitcode=1 +fi + +rm $patch_path.mock $patch_path.tests $patch_path.NEWS +exit $exitcode diff --git a/tools/pre-applypatch b/tools/pre-applypatch new file mode 100755 index 0000000..28ab636 --- /dev/null +++ b/tools/pre-applypatch @@ -0,0 +1,36 @@ +#!/bin/bash +# +# An example hook script to verify what is about to be committed +# by applypatch from an e-mail message. +# +# The hook should exit with non-zero status after issuing an +# appropriate message if it wants to stop the commit. +# +# To enable this hook, rename this file to "pre-applypatch". + +set -eu + +#. git-sh-setup +echo "** in hook **" + +function test_version { + version=$1 + host=$(ls ~/.virtualenvs/mock-$version-* -d | sed -e "s/^.*mock-$version-//") + if [ -z "$host" ]; then + echo "No host found for $version" + return 1 + fi + echo testing $version in virtualenv mock-$version-$host on ssh host $host + ssh $host "cd work/mock && . ~/.virtualenvs/mock-$version-$host/bin/activate && pip install .[test] && unit2" +} + +find . -name "*.pyc" -exec rm "{}" \; + +test_version 2.7 +test_version 3.3 +test_version 3.4 +test_version 3.5 +test_version cpython +test_version pypy + +echo '** pre-apply complete and successful **' @@ -1,12 +1,22 @@ [tox] -envlist = py27,pypy,py34,py35,py36,py37,docs +envlist = py27,pypy,py33,jython [testenv] -commands = - {envbindir}/pytest {posargs} +deps=unittest2 +commands={envbindir}/unit2 discover [] -[testenv:docs] +[testenv:py27] +commands= + {envbindir}/unit2 discover [] + {envbindir}/sphinx-build -E -b doctest docs html deps = + unittest2 sphinx -commands = - {envbindir}/python setup.py build_sphinx + +[testenv:py33] +commands= + {envbindir}/python -m unittest discover [] +deps = + +# note for jython. Execute in tests directory: +# rm `find . -name '*$py.class'`
\ No newline at end of file diff --git a/unittest.cfg b/unittest.cfg new file mode 100644 index 0000000..b2d6f67 --- /dev/null +++ b/unittest.cfg @@ -0,0 +1,95 @@ + +[unittest] +plugins = + unittest2.plugins.debugger + unittest2.plugins.checker + unittest2.plugins.doctestloader + unittest2.plugins.matchregexp + unittest2.plugins.moduleloading + unittest2.plugins.testcoverage + unittest2.plugins.growl + unittest2.plugins.filtertests + unittest2.plugins.junitxml + unittest2.plugins.timed + unittest2.plugins.counttests + unittest2.plugins.logchannels + +excluded-plugins = + +# 0, 1 or 2 (default is 1) +# quiet, normal or verbose +# can be overriden at command line +verbosity = normal + +# true or false +# even if false can be switched on at command line +catch = +buffer = +failfast = + + +[matchregexp] +always-on = False +full-path = True + +[debugger] +always-on = False +errors-only = True + +[coverage] +always-on = False +config = +report-html = False +# only used if report-html is false +annotate = False +# defaults to './htmlcov/' +html-directory = +# if unset will output to console +text-file = +branch = False +timid = False +cover-pylib = False +exclude-lines = + # Have to re-enable the standard pragma + pragma: no cover + + # Don't complain about missing debug-only code: + def __repr__ + if self\.debug + + # Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + + # Don't complain if non-runnable code isn't run: + if 0: + if __name__ == .__main__. + +ignore-errors = False +modules = + +[growl] +always-on = False + +[doctest] +always-on = False + +[module-loading] +always-on = False + +[checker] +always-on = False +pep8 = False +pyflakes = True + +[junit-xml] +always-on = False +path = junit.xml + +[timed] +always-on = True +threshold = 0.01 + +[count] +always-on = True +enhanced = False |