[3.8] bpo-38163: Child mocks detect their type as sync or async (GH-16471) (GH-16484)
This commit is contained in:
parent
36e7e4aabb
commit
21f24ead90
|
@ -865,7 +865,7 @@ object::
|
||||||
True
|
True
|
||||||
|
|
||||||
The result of ``mock()`` is an async function which will have the outcome
|
The result of ``mock()`` is an async function which will have the outcome
|
||||||
of ``side_effect`` or ``return_value``:
|
of ``side_effect`` or ``return_value`` after it has been awaited:
|
||||||
|
|
||||||
- if ``side_effect`` is a function, the async function will return the
|
- if ``side_effect`` is a function, the async function will return the
|
||||||
result of that function,
|
result of that function,
|
||||||
|
@ -890,6 +890,32 @@ object::
|
||||||
>>> mock() # doctest: +SKIP
|
>>> mock() # doctest: +SKIP
|
||||||
<coroutine object AsyncMockMixin._mock_call at ...>
|
<coroutine object AsyncMockMixin._mock_call at ...>
|
||||||
|
|
||||||
|
|
||||||
|
Setting the *spec* of a :class:`Mock`, :class:`MagicMock`, or :class:`AsyncMock`
|
||||||
|
to a class with asynchronous and synchronous functions will automatically
|
||||||
|
detect the synchronous functions and set them as :class:`MagicMock` (if the
|
||||||
|
parent mock is :class:`AsyncMock` or :class:`MagicMock`) or :class:`Mock` (if
|
||||||
|
the parent mock is :class:`Mock`). All asynchronous functions will be
|
||||||
|
:class:`AsyncMock`.
|
||||||
|
|
||||||
|
>>> class ExampleClass:
|
||||||
|
... def sync_foo():
|
||||||
|
... pass
|
||||||
|
... async def async_foo():
|
||||||
|
... pass
|
||||||
|
...
|
||||||
|
>>> a_mock = AsyncMock(ExampleClass)
|
||||||
|
>>> a_mock.sync_foo
|
||||||
|
<MagicMock name='mock.sync_foo' id='...'>
|
||||||
|
>>> a_mock.async_foo
|
||||||
|
<AsyncMock name='mock.async_foo' id='...'>
|
||||||
|
>>> mock = Mock(ExampleClass)
|
||||||
|
>>> mock.sync_foo
|
||||||
|
<Mock name='mock.sync_foo' id='...'>
|
||||||
|
>>> mock.async_foo
|
||||||
|
<AsyncMock name='mock.async_foo' id='...'>
|
||||||
|
|
||||||
|
|
||||||
.. method:: assert_awaited()
|
.. method:: assert_awaited()
|
||||||
|
|
||||||
Assert that the mock was awaited at least once. Note that this is separate
|
Assert that the mock was awaited at least once. Note that this is separate
|
||||||
|
|
|
@ -997,8 +997,9 @@ class NonCallableMock(Base):
|
||||||
# Any asynchronous magic becomes an AsyncMock
|
# Any asynchronous magic becomes an AsyncMock
|
||||||
klass = AsyncMock
|
klass = AsyncMock
|
||||||
elif issubclass(_type, AsyncMockMixin):
|
elif issubclass(_type, AsyncMockMixin):
|
||||||
if _new_name in _all_sync_magics:
|
if (_new_name in _all_sync_magics or
|
||||||
# Any synchronous magic becomes a MagicMock
|
self._mock_methods and _new_name in self._mock_methods):
|
||||||
|
# Any synchronous method on AsyncMock becomes a MagicMock
|
||||||
klass = MagicMock
|
klass = MagicMock
|
||||||
else:
|
else:
|
||||||
klass = AsyncMock
|
klass = AsyncMock
|
||||||
|
|
|
@ -3,7 +3,7 @@ import inspect
|
||||||
import re
|
import re
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from unittest.mock import (ANY, call, AsyncMock, patch, MagicMock,
|
from unittest.mock import (ANY, call, AsyncMock, patch, MagicMock, Mock,
|
||||||
create_autospec, sentinel, _CallList)
|
create_autospec, sentinel, _CallList)
|
||||||
|
|
||||||
|
|
||||||
|
@ -228,33 +228,50 @@ class AsyncAutospecTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
class AsyncSpecTest(unittest.TestCase):
|
class AsyncSpecTest(unittest.TestCase):
|
||||||
def test_spec_as_async_positional_magicmock(self):
|
def test_spec_normal_methods_on_class(self):
|
||||||
mock = MagicMock(async_func)
|
def inner_test(mock_type):
|
||||||
self.assertIsInstance(mock, MagicMock)
|
mock = mock_type(AsyncClass)
|
||||||
m = mock()
|
self.assertIsInstance(mock.async_method, AsyncMock)
|
||||||
self.assertTrue(inspect.isawaitable(m))
|
self.assertIsInstance(mock.normal_method, MagicMock)
|
||||||
asyncio.run(m)
|
|
||||||
|
|
||||||
def test_spec_as_async_kw_magicmock(self):
|
for mock_type in [AsyncMock, MagicMock]:
|
||||||
mock = MagicMock(spec=async_func)
|
with self.subTest(f"test method types with {mock_type}"):
|
||||||
self.assertIsInstance(mock, MagicMock)
|
inner_test(mock_type)
|
||||||
m = mock()
|
|
||||||
self.assertTrue(inspect.isawaitable(m))
|
|
||||||
asyncio.run(m)
|
|
||||||
|
|
||||||
def test_spec_as_async_kw_AsyncMock(self):
|
def test_spec_normal_methods_on_class_with_mock(self):
|
||||||
mock = AsyncMock(spec=async_func)
|
mock = Mock(AsyncClass)
|
||||||
self.assertIsInstance(mock, AsyncMock)
|
self.assertIsInstance(mock.async_method, AsyncMock)
|
||||||
m = mock()
|
self.assertIsInstance(mock.normal_method, Mock)
|
||||||
self.assertTrue(inspect.isawaitable(m))
|
|
||||||
asyncio.run(m)
|
|
||||||
|
|
||||||
def test_spec_as_async_positional_AsyncMock(self):
|
def test_spec_mock_type_kw(self):
|
||||||
mock = AsyncMock(async_func)
|
def inner_test(mock_type):
|
||||||
self.assertIsInstance(mock, AsyncMock)
|
async_mock = mock_type(spec=async_func)
|
||||||
m = mock()
|
self.assertIsInstance(async_mock, mock_type)
|
||||||
self.assertTrue(inspect.isawaitable(m))
|
with self.assertWarns(RuntimeWarning):
|
||||||
asyncio.run(m)
|
# Will raise a warning because never awaited
|
||||||
|
self.assertTrue(inspect.isawaitable(async_mock()))
|
||||||
|
|
||||||
|
sync_mock = mock_type(spec=normal_func)
|
||||||
|
self.assertIsInstance(sync_mock, mock_type)
|
||||||
|
|
||||||
|
for mock_type in [AsyncMock, MagicMock, Mock]:
|
||||||
|
with self.subTest(f"test spec kwarg with {mock_type}"):
|
||||||
|
inner_test(mock_type)
|
||||||
|
|
||||||
|
def test_spec_mock_type_positional(self):
|
||||||
|
def inner_test(mock_type):
|
||||||
|
async_mock = mock_type(async_func)
|
||||||
|
self.assertIsInstance(async_mock, mock_type)
|
||||||
|
with self.assertWarns(RuntimeWarning):
|
||||||
|
# Will raise a warning because never awaited
|
||||||
|
self.assertTrue(inspect.isawaitable(async_mock()))
|
||||||
|
|
||||||
|
sync_mock = mock_type(normal_func)
|
||||||
|
self.assertIsInstance(sync_mock, mock_type)
|
||||||
|
|
||||||
|
for mock_type in [AsyncMock, MagicMock, Mock]:
|
||||||
|
with self.subTest(f"test spec positional with {mock_type}"):
|
||||||
|
inner_test(mock_type)
|
||||||
|
|
||||||
def test_spec_as_normal_kw_AsyncMock(self):
|
def test_spec_as_normal_kw_AsyncMock(self):
|
||||||
mock = AsyncMock(spec=normal_func)
|
mock = AsyncMock(spec=normal_func)
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
Child mocks will now detect their type as either synchronous or
|
||||||
|
asynchronous, asynchronous child mocks will be AsyncMocks and synchronous
|
||||||
|
child mocks will be either MagicMock or Mock (depending on their parent
|
||||||
|
type).
|
Loading…
Reference in New Issue