]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-23078: Add support for {class,static}method to mock.create_autospec() (GH-11613)
authorBerker Peksag <berker.peksag@gmail.com>
Mon, 22 Apr 2019 03:07:56 +0000 (06:07 +0300)
committerGitHub <noreply@github.com>
Mon, 22 Apr 2019 03:07:56 +0000 (06:07 +0300)
Co-authored-by: Felipe <felipe.nospam.ochoa@gmail.com>
(cherry picked from commit 9b21856b0fcda949de239edc7aa6cf3f2f4f77a3)

Lib/unittest/mock.py
Lib/unittest/test/testmock/testhelpers.py
Lib/unittest/test/testmock/testmock.py
Lib/unittest/test/testmock/testpatch.py
Misc/NEWS.d/next/Library/2019-01-18-23-10-10.bpo-23078.l4dFoj.rst [new file with mode: 0644]

index 373e1d5f64d87b347efcad92d9ce12c1a579f3cd..db99585c33d24a35ab41ef64146c89e13896fd43 100644 (file)
@@ -29,7 +29,7 @@ import inspect
 import pprint
 import sys
 import builtins
-from types import ModuleType
+from types import ModuleType, MethodType
 from functools import wraps, partial
 
 
@@ -121,6 +121,8 @@ def _copy_func_details(func, funcopy):
 def _callable(obj):
     if isinstance(obj, type):
         return True
+    if isinstance(obj, (staticmethod, classmethod, MethodType)):
+        return _callable(obj.__func__)
     if getattr(obj, '__call__', None) is not None:
         return True
     return False
index 745580ef79db9073008637a0096b72a593828033..674de367e877aa2ada0ba8aef3b27c1c65381bca 100644 (file)
@@ -5,7 +5,7 @@ import unittest
 
 from unittest.mock import (
     call, _Call, create_autospec, MagicMock,
-    Mock, ANY, _CallList, patch, PropertyMock
+    Mock, ANY, _CallList, patch, PropertyMock, _callable
 )
 
 from datetime import datetime
@@ -1002,5 +1002,43 @@ class TestCallList(unittest.TestCase):
         self.assertNotIsInstance(returned, PropertyMock)
 
 
+class TestCallablePredicate(unittest.TestCase):
+
+    def test_type(self):
+        for obj in [str, bytes, int, list, tuple, SomeClass]:
+            self.assertTrue(_callable(obj))
+
+    def test_call_magic_method(self):
+        class Callable:
+            def __call__(self):
+                pass
+        instance = Callable()
+        self.assertTrue(_callable(instance))
+
+    def test_staticmethod(self):
+        class WithStaticMethod:
+            @staticmethod
+            def staticfunc():
+                pass
+        self.assertTrue(_callable(WithStaticMethod.staticfunc))
+
+    def test_non_callable_staticmethod(self):
+        class BadStaticMethod:
+            not_callable = staticmethod(None)
+        self.assertFalse(_callable(BadStaticMethod.not_callable))
+
+    def test_classmethod(self):
+        class WithClassMethod:
+            @classmethod
+            def classfunc(cls):
+                pass
+        self.assertTrue(_callable(WithClassMethod.classfunc))
+
+    def test_non_callable_classmethod(self):
+        class BadClassMethod:
+            not_callable = classmethod(None)
+        self.assertFalse(_callable(BadClassMethod.not_callable))
+
+
 if __name__ == '__main__':
     unittest.main()
index 2f50236d1ece1a0f2de5e0f23066d2965b695d60..dab17651e057c239756187f90196b3286a070b2d 100644 (file)
@@ -1404,6 +1404,23 @@ class MockTest(unittest.TestCase):
         m = mock.create_autospec(object(), name='sweet_func')
         self.assertIn('sweet_func', repr(m))
 
+    #Issue23078
+    def test_create_autospec_classmethod_and_staticmethod(self):
+        class TestClass:
+            @classmethod
+            def class_method(cls):
+                pass
+
+            @staticmethod
+            def static_method():
+                pass
+        for method in ('class_method', 'static_method'):
+            with self.subTest(method=method):
+                mock_method = mock.create_autospec(getattr(TestClass, method))
+                mock_method()
+                mock_method.assert_called_once_with()
+                self.assertRaises(TypeError, mock_method, 'extra_arg')
+
     #Issue21238
     def test_mock_unsafe(self):
         m = Mock()
index c484adb605086a576b4bc84f802d0d9dddde540d..6358154b3e5e6a2a8334c6d588be8be713985f12 100644 (file)
@@ -51,6 +51,14 @@ class Foo(object):
         pass
     foo = 'bar'
 
+    @staticmethod
+    def static_method():
+        return 24
+
+    @classmethod
+    def class_method(cls):
+        return 42
+
     class Bar(object):
         def a(self):
             pass
@@ -1015,6 +1023,18 @@ class PatchTest(unittest.TestCase):
         self.assertEqual(result, 3)
 
 
+    def test_autospec_staticmethod(self):
+        with patch('%s.Foo.static_method' % __name__, autospec=True) as method:
+            Foo.static_method()
+            method.assert_called_once_with()
+
+
+    def test_autospec_classmethod(self):
+        with patch('%s.Foo.class_method' % __name__, autospec=True) as method:
+            Foo.class_method()
+            method.assert_called_once_with()
+
+
     def test_autospec_with_new(self):
         patcher = patch('%s.function' % __name__, new=3, autospec=True)
         self.assertRaises(TypeError, patcher.start)
diff --git a/Misc/NEWS.d/next/Library/2019-01-18-23-10-10.bpo-23078.l4dFoj.rst b/Misc/NEWS.d/next/Library/2019-01-18-23-10-10.bpo-23078.l4dFoj.rst
new file mode 100644 (file)
index 0000000..975cc9c
--- /dev/null
@@ -0,0 +1,2 @@
+Add support for :func:`classmethod` and :func:`staticmethod` to
+:func:`unittest.mock.create_autospec`.  Initial patch by Felipe Ochoa.