summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLisa Roach <lisaroach14@gmail.com>2019-09-29 22:22:44 -0700
committerGitHub <noreply@github.com>2019-09-29 22:22:44 -0700
commit21f24ead90c22d0e2c2ebf14a64b37d99de54b33 (patch)
treec65cd3d5174adbbffa1772cc7b822e66289eb597
parent36e7e4aabb662e86e9dace1a6447492f45868654 (diff)
downloadcpython-git-21f24ead90c22d0e2c2ebf14a64b37d99de54b33.tar.gz
[3.8] bpo-38163: Child mocks detect their type as sync or async (GH-16471) (GH-16484)
-rw-r--r--Doc/library/unittest.mock.rst28
-rw-r--r--Lib/unittest/mock.py5
-rw-r--r--Lib/unittest/test/testmock/testasync.py67
-rw-r--r--Misc/NEWS.d/next/Library/2019-09-28-20-16-40.bpo-38163.x51-vK.rst4
4 files changed, 76 insertions, 28 deletions
diff --git a/Doc/library/unittest.mock.rst b/Doc/library/unittest.mock.rst
index 300f28c8e2..3fa3cfdaf2 100644
--- a/Doc/library/unittest.mock.rst
+++ b/Doc/library/unittest.mock.rst
@@ -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
diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py
index 5d32f88c6b..488ab1c23d 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -997,8 +997,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
diff --git a/Lib/unittest/test/testmock/testasync.py b/Lib/unittest/test/testmock/testasync.py
index ed8aefc2a8..7671d55a8c 100644
--- a/Lib/unittest/test/testmock/testasync.py
+++ b/Lib/unittest/test/testmock/testasync.py
@@ -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)
@@ -228,33 +228,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)
diff --git a/Misc/NEWS.d/next/Library/2019-09-28-20-16-40.bpo-38163.x51-vK.rst b/Misc/NEWS.d/next/Library/2019-09-28-20-16-40.bpo-38163.x51-vK.rst
new file mode 100644
index 0000000000..5f7db26e05
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2019-09-28-20-16-40.bpo-38163.x51-vK.rst
@@ -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).