|
1 | 1 | import asyncio |
2 | 2 | import copy |
| 3 | +import functools |
3 | 4 | import re |
4 | 5 | import sys |
5 | 6 | import tempfile |
@@ -30,7 +31,8 @@ def next(self): |
30 | 31 |
|
31 | 32 |
|
32 | 33 | class Something(object): |
33 | | - def meth(self, a, b, c, d=None): pass |
| 34 | + def meth(self, a, b, c, d=None): |
| 35 | + return a, b, c, d |
34 | 36 |
|
35 | 37 | @classmethod |
36 | 38 | def cmeth(cls, a, b, c, d=None): pass |
@@ -66,7 +68,21 @@ class Typos(): |
66 | 68 | set_spec = None |
67 | 69 |
|
68 | 70 |
|
69 | | -def something(a): pass |
| 71 | +def something(a): |
| 72 | + return a |
| 73 | + |
| 74 | + |
| 75 | +def something_two_args(a, b): |
| 76 | + return a, b |
| 77 | + |
| 78 | + |
| 79 | +class SomethingWithProps(object): |
| 80 | + def __init__(self, a, b, c=None): |
| 81 | + self.a = a |
| 82 | + self.b = b |
| 83 | + self.c = c |
| 84 | + |
| 85 | + def meth(self, x, y): pass |
70 | 86 |
|
71 | 87 |
|
72 | 88 | class MockTest(unittest.TestCase): |
@@ -382,6 +398,17 @@ def test_reset_mock(self): |
382 | 398 | "children incorrectly cleared") |
383 | 399 | self.assertFalse(mock.something.called, "child not reset") |
384 | 400 |
|
| 401 | + def test_mock_autospec_reset_mock(self): |
| 402 | + mock_something = Mock(autospec=Something) |
| 403 | + mock_something.meth(sentinel.a, sentinel.b, sentinel.c) |
| 404 | + |
| 405 | + mock_something.reset_mock() |
| 406 | + |
| 407 | + self.assertEqual(mock_something.meth.call_count, 0) |
| 408 | + # the child mock is preserved by reset_mock(), so signature checking |
| 409 | + # is still enforced afterwards. |
| 410 | + self.assertRaises(TypeError, mock_something.meth) |
| 411 | + mock_something.meth(sentinel.a, sentinel.b, sentinel.c) |
385 | 412 |
|
386 | 413 | def test_reset_mock_recursion(self): |
387 | 414 | mock = Mock() |
@@ -738,6 +765,12 @@ def test_mock_autospec_all_members(self): |
738 | 765 | self._check_autospeced_something(mock_something) |
739 | 766 |
|
740 | 767 |
|
| 768 | + def test_mock_autospec_all_members_wraps(self): |
| 769 | + something = Something() |
| 770 | + mock_something = Mock(autospec=something, wraps=something) |
| 771 | + self._check_autospeced_something(mock_something) |
| 772 | + |
| 773 | + |
741 | 774 | def _check_autospeced_something_async(self, something): |
742 | 775 | # assert that AttributeError is raised if the method does not exist. |
743 | 776 | self.assertRaises(AttributeError, getattr, something, 'foolish') |
@@ -772,6 +805,140 @@ def test_mock_autospec_all_members_async(self): |
772 | 805 | self._check_autospeced_something_async(mock_something) |
773 | 806 |
|
774 | 807 |
|
| 808 | + @requires_working_socket() |
| 809 | + def test_mock_autospec_all_members_wraps_async(self): |
| 810 | + something = SomethingAsync() |
| 811 | + mock_something = AsyncMock(autospec=something, wraps=something) |
| 812 | + self._check_autospeced_something_async(mock_something) |
| 813 | + |
| 814 | + |
| 815 | + def test_mock_autospec_function(self): |
| 816 | + mock_func = Mock(autospec=something, wraps=something) |
| 817 | + |
| 818 | + result = mock_func(sentinel.a) |
| 819 | + self.assertEqual(result, sentinel.a) |
| 820 | + |
| 821 | + self.assertRaises(TypeError, mock_func) |
| 822 | + self.assertRaises(TypeError, mock_func, sentinel.a, sentinel.b) |
| 823 | + |
| 824 | + |
| 825 | + def test_mock_autospec_partial_function(self): |
| 826 | + partial_something = functools.partial(something_two_args, sentinel.a) |
| 827 | + |
| 828 | + mock_func = Mock(autospec=partial_something, wraps=partial_something) |
| 829 | + |
| 830 | + result = mock_func(sentinel.b) |
| 831 | + self.assertEqual(result, (sentinel.a, sentinel.b)) |
| 832 | + |
| 833 | + self.assertRaises(TypeError, mock_func) |
| 834 | + self.assertRaises(TypeError, mock_func, sentinel.b, sentinel.c) |
| 835 | + |
| 836 | + |
| 837 | + def test_mock_autospec_partial_method(self): |
| 838 | + obj = Something() |
| 839 | + partial_meth = functools.partial(obj.meth, sentinel.a, sentinel.b) |
| 840 | + |
| 841 | + mock_meth = Mock(autospec=partial_meth, wraps=partial_meth) |
| 842 | + |
| 843 | + result = mock_meth(sentinel.c) |
| 844 | + self.assertEqual(result, (sentinel.a, sentinel.b, sentinel.c, None)) |
| 845 | + |
| 846 | + self.assertRaises(TypeError, mock_meth) |
| 847 | + self.assertRaises(TypeError, mock_meth, sentinel.d, e=sentinel.e) |
| 848 | + |
| 849 | + |
| 850 | + def test_mock_autospec_side_effect(self): |
| 851 | + def side_effect(a, b, c, d=None): |
| 852 | + return (a, b, c, d) |
| 853 | + |
| 854 | + mock_something = Mock(autospec=Something) |
| 855 | + mock_something.meth.side_effect = side_effect |
| 856 | + |
| 857 | + result = mock_something.meth(sentinel.a, sentinel.b, sentinel.c) |
| 858 | + self.assertEqual(result, (sentinel.a, sentinel.b, sentinel.c, None)) |
| 859 | + |
| 860 | + # signature checking is enforced before the side_effect runs. |
| 861 | + self.assertRaises(TypeError, mock_something.meth) |
| 862 | + self.assertRaises(TypeError, mock_something.meth, sentinel.a) |
| 863 | + |
| 864 | + |
| 865 | + def test_mock_autospec_class_init_signature(self): |
| 866 | + mock_class = Mock(autospec=SomethingWithProps) |
| 867 | + |
| 868 | + mock_class(sentinel.a, sentinel.b) |
| 869 | + mock_class(sentinel.a, sentinel.b, sentinel.c) |
| 870 | + |
| 871 | + self.assertRaises(TypeError, mock_class) |
| 872 | + self.assertRaises(TypeError, mock_class, sentinel.a) |
| 873 | + self.assertRaises(TypeError, mock_class, sentinel.a, sentinel.b, |
| 874 | + sentinel.c, e=sentinel.e) |
| 875 | + |
| 876 | + |
| 877 | + def test_mock_autospec_with_spec_list_for_init_only_attributes(self): |
| 878 | + # SomethingWithProps only assigns 'a' and 'b' as instance attributes |
| 879 | + # in __init__, so they are absent from dir(SomethingWithProps) and |
| 880 | + # would normally be rejected by autospec. |
| 881 | + mock_something = Mock( |
| 882 | + autospec=SomethingWithProps, spec=['a', 'b', 'c']) |
| 883 | + |
| 884 | + # the extra attributes from `spec` are accessible. |
| 885 | + mock_something.a |
| 886 | + mock_something.b |
| 887 | + |
| 888 | + # autospec is still applied: methods are still signature-checked. |
| 889 | + mock_something.meth(sentinel.x, sentinel.y) |
| 890 | + self.assertRaises(TypeError, mock_something.meth) |
| 891 | + |
| 892 | + # attributes that are neither on the spec class nor in the extra |
| 893 | + # `spec` list are still rejected. |
| 894 | + self.assertRaises(AttributeError, getattr, mock_something, 'foolish') |
| 895 | + |
| 896 | + |
| 897 | + def test_mock_autospec_nested(self): |
| 898 | + class Inner(object): |
| 899 | + def meth(self, a, b): pass |
| 900 | + |
| 901 | + class Outer(object): |
| 902 | + inner = Inner() |
| 903 | + |
| 904 | + mock_outer = Mock(autospec=Outer) |
| 905 | + self.assertIsInstance(mock_outer.inner, Mock) |
| 906 | + |
| 907 | + mock_outer.inner.meth(sentinel.a, sentinel.b) |
| 908 | + self.assertRaises(TypeError, mock_outer.inner.meth) |
| 909 | + self.assertRaises(TypeError, mock_outer.inner.meth, sentinel.a) |
| 910 | + |
| 911 | + |
| 912 | + def test_mock_autospec_magic_methods(self): |
| 913 | + for Klass in MagicMock, NonCallableMagicMock: |
| 914 | + mock = Klass(autospec=int) |
| 915 | + int(mock) |
| 916 | + |
| 917 | + mock.__int__.return_value = 4 |
| 918 | + self.assertEqual(int(mock), 4) |
| 919 | + |
| 920 | + # attributes that don't exist on the spec are still rejected. |
| 921 | + self.assertRaises(AttributeError, getattr, mock, 'foo') |
| 922 | + |
| 923 | + |
| 924 | + def test_mock_autospec_call_signature(self): |
| 925 | + class Caller(object): |
| 926 | + def __init__(self, a): pass |
| 927 | + def __call__(self, x): pass |
| 928 | + |
| 929 | + # autospeccing the class checks the constructor signature. |
| 930 | + mock_cls = MagicMock(autospec=Caller) |
| 931 | + mock_cls(a=sentinel.a) |
| 932 | + self.assertRaises(TypeError, mock_cls) |
| 933 | + self.assertRaises(TypeError, mock_cls, sentinel.a, sentinel.b) |
| 934 | + |
| 935 | + # autospeccing an instance checks the __call__ signature instead. |
| 936 | + mock_instance = MagicMock(autospec=Caller(sentinel.a)) |
| 937 | + mock_instance(x=sentinel.x) |
| 938 | + self.assertRaises(TypeError, mock_instance) |
| 939 | + self.assertRaises(TypeError, mock_instance, sentinel.x, sentinel.y) |
| 940 | + |
| 941 | + |
775 | 942 | def test_wraps_calls(self): |
776 | 943 | real = Mock() |
777 | 944 |
|
@@ -2262,6 +2429,19 @@ def test_attach_mock_return_value(self): |
2262 | 2429 | self.assertEqual(m.mock_calls, call().foo().call_list()) |
2263 | 2430 |
|
2264 | 2431 |
|
| 2432 | + def test_mock_autospec_attach_mock(self): |
| 2433 | + m = Mock() |
| 2434 | + child = Mock(autospec=Something) |
| 2435 | + m.attach_mock(child, 'child') |
| 2436 | + |
| 2437 | + child.meth(sentinel.a, sentinel.b, sentinel.c) |
| 2438 | + m.assert_has_calls( |
| 2439 | + [call.child.meth(sentinel.a, sentinel.b, sentinel.c)]) |
| 2440 | + |
| 2441 | + # signature checking survives being attached to another mock. |
| 2442 | + self.assertRaises(TypeError, child.meth) |
| 2443 | + |
| 2444 | + |
2265 | 2445 | def test_attach_mock_patch_autospec(self): |
2266 | 2446 | parent = Mock() |
2267 | 2447 |
|
@@ -2511,6 +2691,13 @@ def test_property_not_called_with_spec_mock(self): |
2511 | 2691 | self.assertIsNone(obj._instance, msg='after mock') |
2512 | 2692 | self.assertEqual('object', obj.instance) |
2513 | 2693 |
|
| 2694 | + def test_mock_autospec_property_not_called(self): |
| 2695 | + obj = SomethingElse() |
| 2696 | + self.assertIsNone(obj._instance, msg='before mock') |
| 2697 | + mock = Mock(autospec=obj) |
| 2698 | + self.assertIsNone(obj._instance) |
| 2699 | + self.assertEqual('object', obj.instance) |
| 2700 | + |
2514 | 2701 | def test_decorated_async_methods_with_spec_mock(self): |
2515 | 2702 | class Foo(): |
2516 | 2703 | @classmethod |
|
0 commit comments