-
-
Notifications
You must be signed in to change notification settings - Fork 34.5k
bpo-26467: Adds AsyncMock for asyncio Mock library support #9296
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
4353041
Initial commmit adding asyncio mock support.
lisroach b83e5a5
Adding async support to the mock library.
lisroach a9ea983
Removes superfluous changes.
lisroach 50581e3
Cleans up comments.
lisroach 96ddb0e
Fixes inspect and attribute error issues.
lisroach a4d4dbc
Fixes test_unittest changing env because of version issue.
lisroach bfdd5a7
Removes newlines from inspect.
lisroach ed7f13c
Removes unneeded comment and newlines.
lisroach 34fa74e
Fixes async tests. Removes inspect fix.
lisroach 302ef64
Fixes environment test issue.
lisroach bf749ac
Adds argument tests.
lisroach 30b64b5
Adding the side_effect exception test.
lisroach 5edac2a
Changes CoroutineMock to AsyncMock. Removes old-style coroutine refer…
lisroach fa978cc
Merge branch 'master' into asyncio_mock
lisroach 24920a6
Changes fnmatch to list comp.
lisroach aec3153
Fixes import and a rebase.
lisroach 45dddb7
Merge branch 'master' of https://github.com/python/cpython into async…
lisroach c0a88a9
Updates news with AsyncMock name change.
lisroach f9bee6e
Removes extraneous comments.
lisroach 81ad0d1
Fixes RunTime warnings and missing io import.
lisroach c260104
Changes check to use issubclass instead of !=.
lisroach ae13db1
Adds AsyncMock docs and tests for iterators and context managers.
lisroach 68dff1b
Uncomments commented out test.
lisroach 64301e2
Fixes based on comments.
lisroach c7cd95e
Fixes broken docs.
lisroach 033f7d3
Fixes broken doc await_arg.
lisroach 2fef02c
Adds shoutout to Martin Richard for asynctest.
lisroach File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -201,9 +201,11 @@ The Mock Class | |||||
|
|
||||||
| .. testsetup:: | ||||||
|
|
||||||
| import asyncio | ||||||
| import inspect | ||||||
| import unittest | ||||||
| from unittest.mock import sentinel, DEFAULT, ANY | ||||||
| from unittest.mock import patch, call, Mock, MagicMock, PropertyMock | ||||||
| from unittest.mock import patch, call, Mock, MagicMock, PropertyMock, AsyncMock | ||||||
| from unittest.mock import mock_open | ||||||
|
|
||||||
| :class:`Mock` is a flexible mock object intended to replace the use of stubs and | ||||||
|
|
@@ -851,6 +853,217 @@ object:: | |||||
| >>> p.assert_called_once_with() | ||||||
|
|
||||||
|
|
||||||
| .. class:: AsyncMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs) | ||||||
|
|
||||||
| An asynchronous version of :class:`Mock`. The :class:`AsyncMock` object will | ||||||
| behave so the object is recognized as an async function, and the result of a | ||||||
| call is an awaitable. | ||||||
|
|
||||||
| >>> mock = AsyncMock() | ||||||
| >>> asyncio.iscoroutinefunction(mock) | ||||||
| True | ||||||
| >>> inspect.isawaitable(mock()) | ||||||
| True | ||||||
|
|
||||||
| The result of ``mock()`` is an async function which will have the outcome | ||||||
| of ``side_effect`` or ``return_value``: | ||||||
|
|
||||||
| - if ``side_effect`` is a function, the async function will return the | ||||||
| result of that function, | ||||||
| - if ``side_effect`` is an exception, the async function will raise the | ||||||
| exception, | ||||||
| - if ``side_effect`` is an iterable, the async function will return the | ||||||
| next value of the iterable, however, if the sequence of result is | ||||||
| exhausted, ``StopIteration`` is raised immediately, | ||||||
| - if ``side_effect`` is not defined, the async function will return the | ||||||
| value defined by ``return_value``, hence, by default, the async function | ||||||
| returns a new :class:`AsyncMock` object. | ||||||
|
|
||||||
|
|
||||||
| Setting the *spec* of a :class:`Mock` or :class:`MagicMock` to an async function | ||||||
| will result in a coroutine object being returned after calling. | ||||||
|
|
||||||
| >>> async def async_func(): pass | ||||||
| ... | ||||||
| >>> mock = MagicMock(async_func) | ||||||
| >>> mock | ||||||
| <MagicMock spec='function' id='...'> | ||||||
| >>> mock() | ||||||
| <coroutine object AsyncMockMixin._mock_call at ...> | ||||||
|
|
||||||
| .. method:: assert_awaited() | ||||||
|
|
||||||
| Assert that the mock was awaited at least once. | ||||||
|
|
||||||
| >>> mock = AsyncMock() | ||||||
| >>> async def main(): | ||||||
| ... await mock() | ||||||
| ... | ||||||
| >>> asyncio.run(main()) | ||||||
| >>> mock.assert_awaited() | ||||||
| >>> mock_2 = AsyncMock() | ||||||
| >>> mock_2.assert_awaited() | ||||||
| Traceback (most recent call last): | ||||||
| ... | ||||||
| AssertionError: Expected mock to have been awaited. | ||||||
|
|
||||||
| .. method:: assert_awaited_once() | ||||||
|
|
||||||
| Assert that the mock was awaited exactly once. | ||||||
|
|
||||||
| >>> mock = AsyncMock() | ||||||
| >>> async def main(): | ||||||
| ... await mock() | ||||||
| ... | ||||||
| >>> asyncio.run(main()) | ||||||
| >>> mock.assert_awaited_once() | ||||||
| >>> asyncio.run(main()) | ||||||
| >>> mock.method.assert_awaited_once() | ||||||
| Traceback (most recent call last): | ||||||
| ... | ||||||
| AssertionError: Expected mock to have been awaited once. Awaited 2 times. | ||||||
|
|
||||||
| .. method:: assert_awaited_with(*args, **kwargs) | ||||||
|
|
||||||
| Assert that the last await was with the specified arguments. | ||||||
|
|
||||||
| >>> mock = AsyncMock() | ||||||
| >>> async def main(*args, **kwargs): | ||||||
| ... await mock(*args, **kwargs) | ||||||
| ... | ||||||
| >>> asyncio.run(main('foo', bar='bar')) | ||||||
| >>> mock.assert_awaited_with('foo', bar='bar') | ||||||
| >>> mock.assert_awaited_with('other') | ||||||
| Traceback (most recent call last): | ||||||
| ... | ||||||
| AssertionError: expected call not found. | ||||||
| Expected: mock('other') | ||||||
| Actual: mock('foo', bar='bar') | ||||||
|
|
||||||
| .. method:: assert_awaited_once_with(*args, **kwargs) | ||||||
|
|
||||||
| Assert that the mock was awaited exactly once and with the specified | ||||||
| arguments. | ||||||
|
|
||||||
| >>> mock = AsyncMock() | ||||||
| >>> async def main(*args, **kwargs): | ||||||
| ... await mock(*args, **kwargs) | ||||||
| ... | ||||||
| >>> asyncio.run(main('foo', bar='bar')) | ||||||
| >>> mock.assert_awaited_once_with('foo', bar='bar') | ||||||
| >>> asyncio.run(main('foo', bar='bar')) | ||||||
| >>> mock.assert_awaited_once_with('foo', bar='bar') | ||||||
| Traceback (most recent call last): | ||||||
| ... | ||||||
| AssertionError: Expected mock to have been awaited once. Awaited 2 times. | ||||||
|
|
||||||
| .. method:: assert_any_await(*args, **kwargs) | ||||||
|
|
||||||
| Assert the mock has ever been awaited with the specified arguments. | ||||||
|
|
||||||
| >>> mock = AsyncMock() | ||||||
| >>> async def main(*args, **kwargs): | ||||||
| ... await mock(*args, **kwargs) | ||||||
| ... | ||||||
| >>> asyncio.run(main('foo', bar='bar')) | ||||||
| >>> asyncio.run(main('hello')) | ||||||
| >>> mock.assert_any_await('foo', bar='bar') | ||||||
| >>> mock.assert_any_await('other') | ||||||
| Traceback (most recent call last): | ||||||
| ... | ||||||
| AssertionError: mock('other') await not found | ||||||
|
|
||||||
| .. method:: assert_has_awaits(calls, any_order=False) | ||||||
|
|
||||||
| Assert the mock has been awaited with the specified calls. | ||||||
| The :attr:`await_args_list` list is checked for the awaits. | ||||||
|
|
||||||
| If *any_order* is False (the default) then the awaits must be | ||||||
| sequential. There can be extra calls before or after the | ||||||
| specified awaits. | ||||||
|
|
||||||
| If *any_order* is True then the awaits can be in any order, but | ||||||
| they must all appear in :attr:`await_args_list`. | ||||||
|
|
||||||
| >>> mock = AsyncMock() | ||||||
| >>> async def main(*args, **kwargs): | ||||||
| ... await mock(*args, **kwargs) | ||||||
| ... | ||||||
| >>> calls = [call("foo"), call("bar")] | ||||||
| >>> mock.assert_has_calls(calls) | ||||||
| Traceback (most recent call last): | ||||||
| ... | ||||||
| AssertionError: Calls not found. | ||||||
| Expected: [call('foo'), call('bar')] | ||||||
| >>> asyncio.run(main('foo')) | ||||||
| >>> asyncio.run(main('bar')) | ||||||
| >>> mock.assert_has_calls(calls) | ||||||
|
|
||||||
| .. method:: assert_not_awaited() | ||||||
|
|
||||||
| Assert that the mock was never awaited. | ||||||
|
|
||||||
| >>> mock = AsyncMock() | ||||||
| >>> mock.assert_not_awaited() | ||||||
|
|
||||||
| .. method:: reset_mock(*args, **kwargs) | ||||||
|
|
||||||
| See :func:`Mock.reset_mock`. Also sets :attr:`await_count` to 0, | ||||||
| :attr:`await_args` to None, and clears the :attr:`await_args_list`. | ||||||
|
|
||||||
| .. attribute:: await_count | ||||||
|
|
||||||
| An integer keeping track of how many times the mock object has been awaited. | ||||||
|
|
||||||
| >>> mock = AsyncMock() | ||||||
| >>> async def main(): | ||||||
| ... await mock() | ||||||
| ... | ||||||
| >>> asyncio.run(main()) | ||||||
| >>> mock.await_count | ||||||
| 1 | ||||||
| >>> asyncio.run(main()) | ||||||
| >>> mock.await_count | ||||||
| 2 | ||||||
|
|
||||||
| .. attribute:: await_args | ||||||
|
|
||||||
| This is either ``None`` (if the mock hasn’t been awaited), or the arguments that | ||||||
| the mock was last awaited with. Functions the same as :attr:`Mock.call_args`. | ||||||
|
|
||||||
| >>> mock = AsyncMock() | ||||||
| >>> async def main(*args): | ||||||
| ... await mock() | ||||||
| ... | ||||||
| >>> mock.await_args | ||||||
| >>> asyncio.run(main('foo')) | ||||||
| >>> mock.await_args | ||||||
| call('foo') | ||||||
| >>> asyncio.run(main('bar')) | ||||||
| >>> mock.await_args | ||||||
| call('bar') | ||||||
|
|
||||||
|
|
||||||
| .. attribute:: await_args_list | ||||||
|
|
||||||
| This is a list of all the awaits made to the mock object in sequence (so the | ||||||
| length of the list is the number of times it has been awaited). Before any | ||||||
| awaits have been made it is an empty list. | ||||||
|
|
||||||
| >>> mock = AsyncMock() | ||||||
| >>> async def main(*args): | ||||||
| ... await mock() | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| ... | ||||||
| >>> mock.await_args_list | ||||||
| [] | ||||||
| >>> asyncio.run(main('foo')) | ||||||
| >>> mock.await_args | ||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| [call('foo')] | ||||||
| >>> asyncio.run(main('bar')) | ||||||
| >>> mock.await_args_list | ||||||
| [call('foo'), call('bar')] | ||||||
|
|
||||||
|
|
||||||
| Calling | ||||||
| ~~~~~~~ | ||||||
|
|
||||||
|
|
||||||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.