8000 bpo-26467: Adds AsyncMock for asyncio Mock library support (GH-9296) · python/cpython@77b3b77 · GitHub
[go: up one dir, main page]

Skip to content

Commit 77b3b77

Browse files
authored
bpo-26467: Adds AsyncMock for asyncio Mock library support (GH-9296)
1 parent 0f72147 commit 77b3b77

File tree

6 files changed

+1161
-20
lines changed

6 files changed

+1161
-20
lines changed

Doc/library/unittest.mock.rst

Lines changed: 214 additions & 1 deletion
< 179B /tr>
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,11 @@ The Mock Class
201201

202202
.. testsetup::
203203

204+
import asyncio
205+
import inspect
204206
import unittest
205207
from unittest.mock import sentinel, DEFAULT, ANY
206-
from unittest.mock import patch, call, Mock, MagicMock, PropertyMock
208+
from unittest.mock import patch, call, Mock, MagicMock, PropertyMock, AsyncMock
207209
from unittest.mock import mock_open
208210

209211
:class:`Mock` is a flexible mock object intended to replace the use of stubs and
@@ -851,6 +853,217 @@ object::
851853
>>> p.assert_called_once_with()
852854

853855

856+
.. class:: AsyncMock(spec=None, side_effect=None, return_value=DEFAULT, wraps=None, name=None, spec_set=None, unsafe=False, **kwargs)
857+
858+
An asynchronous version of :class:`Mock`. The :class:`AsyncMock` object will
859+
behave so the object is recognized as an async function, and the result of a
860+
call is an awaitable.
861+
862+
>>> mock = AsyncMock()
863+
>>> asyncio.iscoroutinefunction(mock)
864+
True
865+
>>> inspect.isawaitable(mock())
866+
True
867+
868+
The result of ``mock()`` is an async function which will have the outcome
869+
of ``side_effect`` or ``return_value``:
870+
871+
- if ``side_effect`` is a function, the async function will return the
872+
result of that function,
873+
- if ``side_effect`` is an exception, the async function will raise the
874+
exception,
875+
- if ``side_effect`` is an iterable, the async function will return the
876+
next value of the iterable, however, if the sequence of result is
877+
exhausted, ``StopIteration`` is raised immediately,
878+
- if ``side_effect`` is not defined, the async function will return the
879+
value defined by ``return_value``, hence, by default, the async function
880+
returns a new :class:`AsyncMock` object.
881+
882+
883+
Setting the *spec* of a :class:`Mock` or :class:`MagicMock` to an async function
884+
will result in a coroutine object being returned after calling.
885+
886+
>>> async def async_func(): pass
887+
...
888+
>>> mock = MagicMock(async_func)
889+
>>> mock
890+
<MagicMock spec='function' id='...'>
891+
>>> mock()
892+
<coroutine object AsyncMockMixin._mock_call at ...>
893+
894+
.. method:: assert_awaited()
895+
896+
Assert that the mock was awaited at least once.
897+
898+
>>> mock = AsyncMock()
899+
>>> async def main():
900+
... await mock()
901+
...
902+
>>> asyncio.run(main())
903+
>>> mock.assert_awaited()
904+
>>> mock_2 = AsyncMock()
905+
>>> mock_2.assert_awaited()
906+
Traceback (most recent call last):
907+
...
908+
AssertionError: Expected mock to have been awaited.
909+
910+
.. method:: assert_awaited_once()
911+
912+
Assert that the mock was awaited exactly once.
913+
914+
>>> mock = AsyncMock()
915+
>>> async def main():
916+
... await mock()
917+
...
918+
>>> asyncio.run(main())
919+
>>> mock.assert_awaited_once()
920+
>>> asyncio.run(main())
921+
>>> mock.method.assert_awaited_once()
922+
Traceback (most recent call last):
923+
...
924+
AssertionError: Expected mock to have been awaited once. Awaited 2 times.
925+
926+
.. method:: assert_awaited_with(*args, **kwargs)
927+
928+
Assert that the last await was with the specified arguments.
929+
930+
>>> mock = AsyncMock()
931+
>>> async def main(*args, **kwargs):
932+
... await mock(*args, **kwargs)
933+
...
934+
>>> asyncio.run(main('foo', bar='bar'))
935+
>>> mock.assert_awaited_with('foo', bar='bar')
936+
>>> mock.assert_awaited_with('other')
937+
Traceback (most recent call last):
938+
...
939+
AssertionError: expected call not found.
940+
Expected: mock('other')
941+
Actual: mock('foo', bar='bar')
942+
943+
.. method:: assert_awaited_once_with(*args, **kwargs)
944+
945+
Assert that the mock was awaited exactly once and with the specified
946+
arguments.
947+
948+
>>> mock = AsyncMock()
949+
>>> async def main(*args, **kwargs):
950+
... await mock(*args, **kwargs)
951+
...
952+
>>> asyncio.run(main('foo', bar='bar'))
953+
>>> mock.assert_awaited_once_with('foo', bar='bar')
954+
>>> asyncio.run(main('foo', bar='bar'))
955+
>>> mock.assert_awaited_once_with('foo', bar='bar')
956+
Traceback (most recent call last):
957+
...
958+
AssertionError: Expected mock to have been awaited once. Awaited 2 times.
959+
960+
.. method:: assert_any_await(*args, **kwargs)
961+
962+
Assert the mock has ever been awaited with the specified arguments.
963+
964+
>>> mock = AsyncMock()
965+
>>> async def main(*args, **kwargs):
966+
... await mock(*args, **kwargs)
967+
...
968+
>>> asyncio.run(main('foo', bar='bar'))
969+
>>> asyncio.run(main('hello'))
970+
>>> mock.assert_any_await('foo', bar='bar')
971+
>>> mock.assert_any_await('other')
972+
Traceback (most recent call last):
973+
...
974+
AssertionError: mock('other') await not found
975+
976+
.. method:: assert_has_awaits(calls, any_order=False)
977+
978+
Assert the mock has been awaited with the specified calls.
979+
The :attr:`await_args_list` list is checked for the awaits.
980+
981+
If *any_order* is False (the default) then the awaits must be
982+
sequential. There can be extra calls before or after the
983+
specified awaits.
984+
985+
If *any_order* is True then the awaits can be in any order, but
986+
they must all appear in :attr:`await_args_list`.
987+
988+
>>> mock = AsyncMock()
989+
>>> async def main(*args, **kwargs):
990+
... await mock(*args, **kwargs)
991+
...
992+
>>> calls = [call("foo"), call("bar")]
993+
>>> mock.assert_has_calls(calls)
994+
Traceback (most recent call last):
995+
...
996+
AssertionError: Calls not found.
997+
Expected: [call('foo'), call('bar')]
998+
>>> asyncio.run(main('foo'))
999+
>>> asyncio.run(main('bar'))
1000+
>>> mock.assert_has_calls(calls)
1001+
1002+
.. method:: assert_not_awaited()
1003+
1004+
Assert that the mock was never awaited.
1005+
1006+
>>> mock = AsyncMock()
1007+
>>> mock.assert_not_awaited()
1008+
1009+
.. method:: reset_mock(*args, **kwargs)
1010+
1011+
See :func:`Mock.reset_mock`. Also sets < F42D span class="pl-k">:attr:`await_count` to 0,
1012+
:attr:`await_args` to None, and clears the :attr:`await_args_list`.
1013+
1014+
.. attribute:: await_count
1015+
1016+
An integer keeping track of how many times the mock object has been awaited.
1017+
1018+
>>> mock = AsyncMock()
1019+
>>> async def main():
1020+
... await mock()
1021+
...
1022+
>>> asyncio.run(main())
1023+
>>> mock.await_count
1024+
1
1025+
>>> asyncio.run(main())
1026+
>>> mock.await_count
1027+
2
1028+
1029+
.. attribute:: await_args
1030+
1031+
This is either ``None`` (if the mock hasn’t been awaited), or the arguments that
1032+
the mock was last awaited with. Functions the same as :attr:`Mock.call_args`.
1033+
1034+
>>> mock = AsyncMock()
1035+
>>> async def main(*args):
1036+
... await mock(*args)
1037+
...
1038+
>>> mock.await_args
1039+
>>> asyncio.run(main('foo'))
1040+
>>> mock.await_args
1041+
call('foo')
1042+
>>> asyncio.run(main('bar'))
1043+
>>> mock.await_args
1044+
call('bar')
1045+
1046+
1047+
.. attribute:: await_args_list
1048+
1049+
This is a list of all the awaits made to the mock object in sequence (so the
1050+
length of the list is the number of times it has been awaited). Before any
1051+
awaits have been made it is an empty list.
1052+
1053+
>>> mock = AsyncMock()
1054+
>>> async def main(*args):
1055+
... await mock(*args)
1056+
...
1057+
>>> mock.await_args_list
1058+
[]
1059+
>>> asyncio.run(main('foo'))
1060+
>>> mock.await_args_list
1061+
[call('foo')]
1062+
>>> asyncio.run(main('bar'))
1063+
>>> mock.await_args_list
1064+
[call('foo'), call('bar')]
1065+
1066+
8541067
Calling
8551068
~~~~~~~
8561069

Doc/whatsnew/3.8.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,10 @@ unicodedata
538538
unittest
539539
--------
540540

541+
* XXX Added :class:`AsyncMock` to support an asynchronous version of :class:`Mock`.
542+
Appropriate new assert functions for testing have been added as well.
543+
(Contributed by Lisa Roach in :issue:`26467`).
544+
541545
* Added :func:`~unittest.addModuleCleanup()` and
542546
:meth:`~unittest.TestCase.addClassCleanup()` to unittest to support
543547
cleanups for :func:`~unittest.setUpModule()` and

0 commit comments

Comments
 (0)
0