bpo-23078: Add support for {class, static}method to mock.create_autospec() (GH-11613)
![](https://secure.gravatar.com/avatar/cc7737cd64a84f1b5c61a160798e97ee.jpg?s=120&d=mm&r=g)
https://github.com/python/cpython/commit/9b21856b0fcda949de239edc7aa6cf3f2f4... commit: 9b21856b0fcda949de239edc7aa6cf3f2f4f77a3 branch: master author: Xtreak <tir.karthi@gmail.com> committer: Berker Peksag <berker.peksag@gmail.com> date: 2019-04-22T05:30:23+03:00 summary: bpo-23078: Add support for {class,static}method to mock.create_autospec() (GH-11613) Co-authored-by: Felipe <felipe.nospam.ochoa@gmail.com> files: A Misc/NEWS.d/next/Library/2019-01-18-23-10-10.bpo-23078.l4dFoj.rst M Lib/unittest/mock.py M Lib/unittest/test/testmock/testhelpers.py M Lib/unittest/test/testmock/testmock.py M Lib/unittest/test/testmock/testpatch.py diff --git a/Lib/unittest/mock.py b/Lib/unittest/mock.py index 0e77f0e48943..1636073ff009 100644 --- a/Lib/unittest/mock.py +++ b/Lib/unittest/mock.py @@ -29,7 +29,7 @@ import pprint import sys import builtins -from types import ModuleType +from types import ModuleType, MethodType from unittest.util import safe_repr from functools import wraps, partial @@ -122,6 +122,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 diff --git a/Lib/unittest/test/testmock/testhelpers.py b/Lib/unittest/test/testmock/testhelpers.py index 9f1bf2676bf5..0d03108aca55 100644 --- a/Lib/unittest/test/testmock/testhelpers.py +++ b/Lib/unittest/test/testmock/testhelpers.py @@ -5,7 +5,7 @@ from unittest.mock import ( call, _Call, create_autospec, MagicMock, - Mock, ANY, _CallList, patch, PropertyMock + Mock, ANY, _CallList, patch, PropertyMock, _callable ) from datetime import datetime @@ -1011,5 +1011,43 @@ def test_propertymock_returnvalue(self): 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() diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py index 37f14c37f47d..bdaebbe66b74 100644 --- a/Lib/unittest/test/testmock/testmock.py +++ b/Lib/unittest/test/testmock/testmock.py @@ -1419,6 +1419,23 @@ def test_create_autospec_with_name(self): 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() diff --git a/Lib/unittest/test/testmock/testpatch.py b/Lib/unittest/test/testmock/testpatch.py index 2c14360b2df5..51c66fec67fc 100644 --- a/Lib/unittest/test/testmock/testpatch.py +++ b/Lib/unittest/test/testmock/testpatch.py @@ -51,6 +51,14 @@ def g(self): pass foo = 'bar' + @staticmethod + def static_method(): + return 24 + + @classmethod + def class_method(cls): + return 42 + class Bar(object): def a(self): pass @@ -1023,6 +1031,18 @@ def test(mock_function): 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 index 000000000000..975cc9c0454c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2019-01-18-23-10-10.bpo-23078.l4dFoj.rst @@ -0,0 +1,2 @@ +Add support for :func:`classmethod` and :func:`staticmethod` to +:func:`unittest.mock.create_autospec`. Initial patch by Felipe Ochoa.
participants (1)
-
Berker Peksag