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:
Chris Withers 2020-01-27 14:55:56 +00:00 committed by GitHub
parent c7dd3c7d87
commit a46575a8f2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 30 additions and 27 deletions

View file

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

View file

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