[Python-checkins] Clarify and fix assertions that mocks have not been awaited (GH-18196)

Chris Withers webhook-mailer at python.org
Mon Jan 27 09:56:05 EST 2020


https://github.com/python/cpython/commit/a46575a8f2ded8b49e26c25bb67192e1500e76ca
commit: a46575a8f2ded8b49e26c25bb67192e1500e76ca
branch: master
author: Chris Withers <chris at withers.org>
committer: GitHub <noreply at github.com>
date: 2020-01-27T14:55:56Z
summary:

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

files:
M Lib/unittest/test/testmock/testasync.py
M Lib/unittest/test/testmock/testmagicmethods.py

diff --git a/Lib/unittest/test/testmock/testasync.py b/Lib/unittest/test/testmock/testasync.py
index 6cba42727af8e..992076db78706 100644
--- a/Lib/unittest/test/testmock/testasync.py
+++ b/Lib/unittest/test/testmock/testasync.py
@@ -1,7 +1,9 @@
 import asyncio
+import gc
 import inspect
 import re
 import unittest
+from contextlib import contextmanager
 
 from asyncio import run, iscoroutinefunction
 from unittest import IsolatedAsyncioTestCase
@@ -52,6 +54,15 @@ def a(self):
 normal_foo_name = f'{__name__}.NormalClass'
 
 
+ at 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):
     def test_is_coroutine_function_patch(self):
         @patch.object(AsyncClass, 'async_method')
@@ -284,8 +295,7 @@ 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
+            with assertNeverAwaited(self):
                 self.assertTrue(inspect.isawaitable(async_mock()))
 
             sync_mock = mock_type(spec=normal_func)
@@ -299,8 +309,7 @@ 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
+            with assertNeverAwaited(self):
                 self.assertTrue(inspect.isawaitable(async_mock()))
 
             sync_mock = mock_type(normal_func)
@@ -747,8 +756,7 @@ def setUp(self):
 
     def test_assert_called_but_not_awaited(self):
         mock = AsyncMock(AsyncClass)
-        with self.assertWarns(RuntimeWarning):
-            # Will raise a warning because never awaited
+        with assertNeverAwaited(self):
             mock.async_method()
         self.assertTrue(iscoroutinefunction(mock.async_method))
         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):
         mock = AsyncMock(AsyncClass)
         coroutine = mock.async_method()
-        with self.assertWarns(RuntimeWarning):
-            # The first call will be awaited so no warning there
-            # But this call will never get awaited, so it will warn here
+        # The first call will be awaited so no warning there
+        # But this call will never get awaited, so it will warn here
+        with assertNeverAwaited(self):
             mock.async_method()
         with self.assertRaises(AssertionError):
             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):
         kalls = [call('foo')]
-        with self.assertWarns(RuntimeWarning):
-            # Will raise a warning because never awaited
+        with assertNeverAwaited(self):
             self.mock('foo')
         self.mock.assert_has_calls(kalls)
         with self.assertRaises(AssertionError):
             self.mock.assert_has_awaits(kalls)
 
     def test_assert_has_mock_calls_on_async_mock_no_spec(self):
-        with self.assertWarns(RuntimeWarning):
-            # Will raise a warning because never awaited
+        with assertNeverAwaited(self):
             self.mock()
         kalls_empty = [('', (), {})]
         self.assertEqual(self.mock.mock_calls, kalls_empty)
 
-        with self.assertWarns(RuntimeWarning):
-            # Will raise a warning because never awaited
+        with assertNeverAwaited(self):
             self.mock('foo')
+        with assertNeverAwaited(self):
             self.mock('baz')
         mock_kalls = ([call(), call('foo'), call('baz')])
         self.assertEqual(self.mock.mock_calls, mock_kalls)
 
     def test_assert_has_mock_calls_on_async_mock_with_spec(self):
         a_class_mock = AsyncMock(AsyncClass)
-        with self.assertWarns(RuntimeWarning):
-            # Will raise a warning because never awaited
+        with assertNeverAwaited(self):
             a_class_mock.async_method()
         kalls_empty = [('', (), {})]
         self.assertEqual(a_class_mock.async_method.mock_calls, kalls_empty)
         self.assertEqual(a_class_mock.mock_calls, [call.async_method()])
 
-        with self.assertWarns(RuntimeWarning):
-            # Will raise a warning because never awaited
+        with assertNeverAwaited(self):
             a_class_mock.async_method(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)]
@@ -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)
 
     def test_async_method_calls_recorded(self):
-        with self.assertWarns(RuntimeWarning):
-            # Will raise warnings because never awaited
+        with assertNeverAwaited(self):
             self.mock.something(3, fish=None)
+        with assertNeverAwaited(self):
             self.mock.something_else.something(6, cake=sentinel.Cake)
 
         self.assertEqual(self.mock.method_calls, [
@@ -889,19 +893,20 @@ def assert_attrs(mock):
                 self.assertEqual(attr, [])
 
         assert_attrs(self.mock)
-        with self.assertWarns(RuntimeWarning):
-            # Will raise warnings because never awaited
+        with assertNeverAwaited(self):
             self.mock()
+        with assertNeverAwaited(self):
             self.mock(1, 2)
+        with assertNeverAwaited(self):
             self.mock(a=3)
 
         self.mock.reset_mock()
         assert_attrs(self.mock)
 
         a_mock = AsyncMock(AsyncClass)
-        with self.assertWarns(RuntimeWarning):
-            # Will raise warnings because never awaited
+        with assertNeverAwaited(self):
             a_mock.async_method()
+        with assertNeverAwaited(self):
             a_mock.async_method(1, a=3)
 
         a_mock.reset_mock()
diff --git a/Lib/unittest/test/testmock/testmagicmethods.py b/Lib/unittest/test/testmock/testmagicmethods.py
index 5690f7a6bbb57..a4feae7e9d3b7 100644
--- a/Lib/unittest/test/testmock/testmagicmethods.py
+++ b/Lib/unittest/test/testmock/testmagicmethods.py
@@ -1,7 +1,6 @@
 import math
 import unittest
 import os
-import sys
 from asyncio import iscoroutinefunction
 from unittest.mock import AsyncMock, Mock, MagicMock, _magics
 
@@ -429,7 +428,6 @@ def _dir(self):
             self.assertEqual(dir(mock), ['foo'])
 
 
-    @unittest.skipIf('PyPy' in sys.version, "This fails differently on pypy")
     def test_bound_methods(self):
         m = Mock()
 



More information about the Python-checkins mailing list