mirror of
https://github.com/python/cpython.git
synced 2025-11-10 10:32:04 +00:00
Clarify and fix assertions that mocks have not been awaited (GH-18196)
- The gc.collect is needed for other implementations, such as pypy - Using context managers over multiple lines will only catch the warning from the first line in the context! - remove a skip for a test that no longer fails on pypy
This commit is contained in:
parent
c7dd3c7d87
commit
a46575a8f2
2 changed files with 30 additions and 27 deletions
|
|
@ -1,7 +1,9 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
|
import gc
|
||||||
import inspect
|
import inspect
|
||||||
import re
|
import re
|
||||||
import unittest
|
import unittest
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from asyncio import run, iscoroutinefunction
|
from asyncio import run, iscoroutinefunction
|
||||||
from unittest import IsolatedAsyncioTestCase
|
from unittest import IsolatedAsyncioTestCase
|
||||||
|
|
@ -52,6 +54,15 @@ def a(self):
|
||||||
normal_foo_name = f'{__name__}.NormalClass'
|
normal_foo_name = f'{__name__}.NormalClass'
|
||||||
|
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def assertNeverAwaited(test):
|
||||||
|
with test.assertWarnsRegex(RuntimeWarning, "was never awaited$"):
|
||||||
|
yield
|
||||||
|
# In non-CPython implementations of Python, this is needed because timely
|
||||||
|
# deallocation is not guaranteed by the garbage collector.
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
|
||||||
class AsyncPatchDecoratorTest(unittest.TestCase):
|
class AsyncPatchDecoratorTest(unittest.TestCase):
|
||||||
def test_is_coroutine_function_patch(self):
|
def test_is_coroutine_function_patch(self):
|
||||||
@patch.object(AsyncClass, 'async_method')
|
@patch.object(AsyncClass, 'async_method')
|
||||||
|
|
@ -284,8 +295,7 @@ def test_spec_mock_type_kw(self):
|
||||||
def inner_test(mock_type):
|
def inner_test(mock_type):
|
||||||
async_mock = mock_type(spec=async_func)
|
async_mock = mock_type(spec=async_func)
|
||||||
self.assertIsInstance(async_mock, mock_type)
|
self.assertIsInstance(async_mock, mock_type)
|
||||||
with self.assertWarns(RuntimeWarning):
|
with assertNeverAwaited(self):
|
||||||
# Will raise a warning because never awaited
|
|
||||||
self.assertTrue(inspect.isawaitable(async_mock()))
|
self.assertTrue(inspect.isawaitable(async_mock()))
|
||||||
|
|
||||||
sync_mock = mock_type(spec=normal_func)
|
sync_mock = mock_type(spec=normal_func)
|
||||||
|
|
@ -299,8 +309,7 @@ def test_spec_mock_type_positional(self):
|
||||||
def inner_test(mock_type):
|
def inner_test(mock_type):
|
||||||
async_mock = mock_type(async_func)
|
async_mock = mock_type(async_func)
|
||||||
self.assertIsInstance(async_mock, mock_type)
|
self.assertIsInstance(async_mock, mock_type)
|
||||||
with self.assertWarns(RuntimeWarning):
|
with assertNeverAwaited(self):
|
||||||
# Will raise a warning because never awaited
|
|
||||||
self.assertTrue(inspect.isawaitable(async_mock()))
|
self.assertTrue(inspect.isawaitable(async_mock()))
|
||||||
|
|
||||||
sync_mock = mock_type(normal_func)
|
sync_mock = mock_type(normal_func)
|
||||||
|
|
@ -747,8 +756,7 @@ async def _await_coroutine(self, coroutine):
|
||||||
|
|
||||||
def test_assert_called_but_not_awaited(self):
|
def test_assert_called_but_not_awaited(self):
|
||||||
mock = AsyncMock(AsyncClass)
|
mock = AsyncMock(AsyncClass)
|
||||||
with self.assertWarns(RuntimeWarning):
|
with assertNeverAwaited(self):
|
||||||
# Will raise a warning because never awaited
|
|
||||||
mock.async_method()
|
mock.async_method()
|
||||||
self.assertTrue(iscoroutinefunction(mock.async_method))
|
self.assertTrue(iscoroutinefunction(mock.async_method))
|
||||||
mock.async_method.assert_called()
|
mock.async_method.assert_called()
|
||||||
|
|
@ -789,9 +797,9 @@ def test_assert_called_and_awaited_at_same_time(self):
|
||||||
def test_assert_called_twice_and_awaited_once(self):
|
def test_assert_called_twice_and_awaited_once(self):
|
||||||
mock = AsyncMock(AsyncClass)
|
mock = AsyncMock(AsyncClass)
|
||||||
coroutine = mock.async_method()
|
coroutine = mock.async_method()
|
||||||
with self.assertWarns(RuntimeWarning):
|
# The first call will be awaited so no warning there
|
||||||
# The first call will be awaited so no warning there
|
# But this call will never get awaited, so it will warn here
|
||||||
# But this call will never get awaited, so it will warn here
|
with assertNeverAwaited(self):
|
||||||
mock.async_method()
|
mock.async_method()
|
||||||
with self.assertRaises(AssertionError):
|
with self.assertRaises(AssertionError):
|
||||||
mock.async_method.assert_awaited()
|
mock.async_method.assert_awaited()
|
||||||
|
|
@ -826,38 +834,34 @@ def test_assert_awaited_but_not_called(self):
|
||||||
|
|
||||||
def test_assert_has_calls_not_awaits(self):
|
def test_assert_has_calls_not_awaits(self):
|
||||||
kalls = [call('foo')]
|
kalls = [call('foo')]
|
||||||
with self.assertWarns(RuntimeWarning):
|
with assertNeverAwaited(self):
|
||||||
# Will raise a warning because never awaited
|
|
||||||
self.mock('foo')
|
self.mock('foo')
|
||||||
self.mock.assert_has_calls(kalls)
|
self.mock.assert_has_calls(kalls)
|
||||||
with self.assertRaises(AssertionError):
|
with self.assertRaises(AssertionError):
|
||||||
self.mock.assert_has_awaits(kalls)
|
self.mock.assert_has_awaits(kalls)
|
||||||
|
|
||||||
def test_assert_has_mock_calls_on_async_mock_no_spec(self):
|
def test_assert_has_mock_calls_on_async_mock_no_spec(self):
|
||||||
with self.assertWarns(RuntimeWarning):
|
with assertNeverAwaited(self):
|
||||||
# Will raise a warning because never awaited
|
|
||||||
self.mock()
|
self.mock()
|
||||||
kalls_empty = [('', (), {})]
|
kalls_empty = [('', (), {})]
|
||||||
self.assertEqual(self.mock.mock_calls, kalls_empty)
|
self.assertEqual(self.mock.mock_calls, kalls_empty)
|
||||||
|
|
||||||
with self.assertWarns(RuntimeWarning):
|
with assertNeverAwaited(self):
|
||||||
# Will raise a warning because never awaited
|
|
||||||
self.mock('foo')
|
self.mock('foo')
|
||||||
|
with assertNeverAwaited(self):
|
||||||
self.mock('baz')
|
self.mock('baz')
|
||||||
mock_kalls = ([call(), call('foo'), call('baz')])
|
mock_kalls = ([call(), call('foo'), call('baz')])
|
||||||
self.assertEqual(self.mock.mock_calls, mock_kalls)
|
self.assertEqual(self.mock.mock_calls, mock_kalls)
|
||||||
|
|
||||||
def test_assert_has_mock_calls_on_async_mock_with_spec(self):
|
def test_assert_has_mock_calls_on_async_mock_with_spec(self):
|
||||||
a_class_mock = AsyncMock(AsyncClass)
|
a_class_mock = AsyncMock(AsyncClass)
|
||||||
with self.assertWarns(RuntimeWarning):
|
with assertNeverAwaited(self):
|
||||||
# Will raise a warning because never awaited
|
|
||||||
a_class_mock.async_method()
|
a_class_mock.async_method()
|
||||||
kalls_empty = [('', (), {})]
|
kalls_empty = [('', (), {})]
|
||||||
self.assertEqual(a_class_mock.async_method.mock_calls, kalls_empty)
|
self.assertEqual(a_class_mock.async_method.mock_calls, kalls_empty)
|
||||||
self.assertEqual(a_class_mock.mock_calls, [call.async_method()])
|
self.assertEqual(a_class_mock.mock_calls, [call.async_method()])
|
||||||
|
|
||||||
with self.assertWarns(RuntimeWarning):
|
with assertNeverAwaited(self):
|
||||||
# Will raise a warning because never awaited
|
|
||||||
a_class_mock.async_method(1, 2, 3, a=4, b=5)
|
a_class_mock.async_method(1, 2, 3, a=4, b=5)
|
||||||
method_kalls = [call(), call(1, 2, 3, a=4, b=5)]
|
method_kalls = [call(), call(1, 2, 3, a=4, b=5)]
|
||||||
mock_kalls = [call.async_method(), call.async_method(1, 2, 3, a=4, b=5)]
|
mock_kalls = [call.async_method(), call.async_method(1, 2, 3, a=4, b=5)]
|
||||||
|
|
@ -865,9 +869,9 @@ def test_assert_has_mock_calls_on_async_mock_with_spec(self):
|
||||||
self.assertEqual(a_class_mock.mock_calls, mock_kalls)
|
self.assertEqual(a_class_mock.mock_calls, mock_kalls)
|
||||||
|
|
||||||
def test_async_method_calls_recorded(self):
|
def test_async_method_calls_recorded(self):
|
||||||
with self.assertWarns(RuntimeWarning):
|
with assertNeverAwaited(self):
|
||||||
# Will raise warnings because never awaited
|
|
||||||
self.mock.something(3, fish=None)
|
self.mock.something(3, fish=None)
|
||||||
|
with assertNeverAwaited(self):
|
||||||
self.mock.something_else.something(6, cake=sentinel.Cake)
|
self.mock.something_else.something(6, cake=sentinel.Cake)
|
||||||
|
|
||||||
self.assertEqual(self.mock.method_calls, [
|
self.assertEqual(self.mock.method_calls, [
|
||||||
|
|
@ -889,19 +893,20 @@ def assert_attrs(mock):
|
||||||
self.assertEqual(attr, [])
|
self.assertEqual(attr, [])
|
||||||
|
|
||||||
assert_attrs(self.mock)
|
assert_attrs(self.mock)
|
||||||
with self.assertWarns(RuntimeWarning):
|
with assertNeverAwaited(self):
|
||||||
# Will raise warnings because never awaited
|
|
||||||
self.mock()
|
self.mock()
|
||||||
|
with assertNeverAwaited(self):
|
||||||
self.mock(1, 2)
|
self.mock(1, 2)
|
||||||
|
with assertNeverAwaited(self):
|
||||||
self.mock(a=3)
|
self.mock(a=3)
|
||||||
|
|
||||||
self.mock.reset_mock()
|
self.mock.reset_mock()
|
||||||
assert_attrs(self.mock)
|
assert_attrs(self.mock)
|
||||||
|
|
||||||
a_mock = AsyncMock(AsyncClass)
|
a_mock = AsyncMock(AsyncClass)
|
||||||
with self.assertWarns(RuntimeWarning):
|
with assertNeverAwaited(self):
|
||||||
# Will raise warnings because never awaited
|
|
||||||
a_mock.async_method()
|
a_mock.async_method()
|
||||||
|
with assertNeverAwaited(self):
|
||||||
a_mock.async_method(1, a=3)
|
a_mock.async_method(1, a=3)
|
||||||
|
|
||||||
a_mock.reset_mock()
|
a_mock.reset_mock()
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import math
|
import math
|
||||||
import unittest
|
import unittest
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
from asyncio import iscoroutinefunction
|
from asyncio import iscoroutinefunction
|
||||||
from unittest.mock import AsyncMock, Mock, MagicMock, _magics
|
from unittest.mock import AsyncMock, Mock, MagicMock, _magics
|
||||||
|
|
||||||
|
|
@ -429,7 +428,6 @@ def _dir(self):
|
||||||
self.assertEqual(dir(mock), ['foo'])
|
self.assertEqual(dir(mock), ['foo'])
|
||||||
|
|
||||||
|
|
||||||
@unittest.skipIf('PyPy' in sys.version, "This fails differently on pypy")
|
|
||||||
def test_bound_methods(self):
|
def test_bound_methods(self):
|
||||||
m = Mock()
|
m = Mock()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue