bpo-38163: Child mocks detect their type as sync or async (GH-16471)

This commit is contained in:
Lisa Roach 2019-09-29 21:56:47 -07:00 committed by GitHub
parent 5bcc6d89bc
commit 3667e1ee6c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 28 deletions

View File

@ -865,7 +865,7 @@ object::
True
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
result of that function,
@ -890,6 +890,32 @@ object::
>>> mock() # doctest: +SKIP
<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()
Assert that the mock was awaited at least once. Note that this is separate

View File

@ -992,8 +992,9 @@ class NonCallableMock(Base):
# Any asynchronous magic becomes an AsyncMock
klass = AsyncMock
elif issubclass(_type, AsyncMockMixin):
if _new_name in _all_sync_magics:
# Any synchronous magic becomes a MagicMock
if (_new_name in _all_sync_magics or
self._mock_methods and _new_name in self._mock_methods):
# Any synchronous method on AsyncMock becomes a MagicMock
klass = MagicMock
else:
klass = AsyncMock

View File

@ -3,7 +3,7 @@ import inspect
import re
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)
@ -232,33 +232,50 @@ class AsyncAutospecTest(unittest.TestCase):
class AsyncSpecTest(unittest.TestCase):
def test_spec_as_async_positional_magicmock(self):
mock = MagicMock(async_func)
self.assertIsInstance(mock, MagicMock)
m = mock()
self.assertTrue(inspect.isawaitable(m))
asyncio.run(m)
def test_spec_normal_methods_on_class(self):
def inner_test(mock_type):
mock = mock_type(AsyncClass)
self.assertIsInstance(mock.async_method, AsyncMock)
self.assertIsInstance(mock.normal_method, MagicMock)
def test_spec_as_async_kw_magicmock(self):
mock = MagicMock(spec=async_func)
self.assertIsInstance(mock, MagicMock)
m = mock()
self.assertTrue(inspect.isawaitable(m))
asyncio.run(m)
for mock_type in [AsyncMock, MagicMock]:
with self.subTest(f"test method types with {mock_type}"):
inner_test(mock_type)
def test_spec_as_async_kw_AsyncMock(self):
mock = AsyncMock(spec=async_func)
self.assertIsInstance(mock, AsyncMock)
m = mock()
self.assertTrue(inspect.isawaitable(m))
asyncio.run(m)
def test_spec_normal_methods_on_class_with_mock(self):
mock = Mock(AsyncClass)
self.assertIsInstance(mock.async_method, AsyncMock)
self.assertIsInstance(mock.normal_method, Mock)
def test_spec_as_async_positional_AsyncMock(self):
mock = AsyncMock(async_func)
self.assertIsInstance(mock, AsyncMock)
m = mock()
self.assertTrue(inspect.isawaitable(m))
asyncio.run(m)
def test_spec_mock_type_kw(self):
def inner_test(mock_type):
async_mock = mock_type(spec=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(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):
mock = AsyncMock(spec=normal_func)

View File

@ -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).