mirror of
https://github.com/python/cpython.git
synced 2025-11-10 10:32:04 +00:00
gh-135368: Fix mocks on dataclass specs with instance=True (#135421)
* gh-135368: Fix mocks on dataclass specs with `instance=True` * Extend dataclass mock_methods --------- Co-authored-by: Marc Mueller <30130371+cdce8p@users.noreply.github.com>
This commit is contained in:
parent
c2bb3f9843
commit
c8319a3fea
3 changed files with 34 additions and 2 deletions
|
|
@ -1050,6 +1050,7 @@ def __post_init__(self):
|
||||||
create_autospec(WithPostInit()),
|
create_autospec(WithPostInit()),
|
||||||
]:
|
]:
|
||||||
with self.subTest(mock=mock):
|
with self.subTest(mock=mock):
|
||||||
|
self.assertIsInstance(mock, WithPostInit)
|
||||||
self.assertIsInstance(mock.a, int)
|
self.assertIsInstance(mock.a, int)
|
||||||
self.assertIsInstance(mock.b, int)
|
self.assertIsInstance(mock.b, int)
|
||||||
|
|
||||||
|
|
@ -1072,6 +1073,7 @@ class WithDefault:
|
||||||
create_autospec(WithDefault(1)),
|
create_autospec(WithDefault(1)),
|
||||||
]:
|
]:
|
||||||
with self.subTest(mock=mock):
|
with self.subTest(mock=mock):
|
||||||
|
self.assertIsInstance(mock, WithDefault)
|
||||||
self.assertIsInstance(mock.a, int)
|
self.assertIsInstance(mock.a, int)
|
||||||
self.assertIsInstance(mock.b, int)
|
self.assertIsInstance(mock.b, int)
|
||||||
|
|
||||||
|
|
@ -1087,6 +1089,7 @@ def b(self) -> int:
|
||||||
create_autospec(WithMethod(1)),
|
create_autospec(WithMethod(1)),
|
||||||
]:
|
]:
|
||||||
with self.subTest(mock=mock):
|
with self.subTest(mock=mock):
|
||||||
|
self.assertIsInstance(mock, WithMethod)
|
||||||
self.assertIsInstance(mock.a, int)
|
self.assertIsInstance(mock.a, int)
|
||||||
mock.b.assert_not_called()
|
mock.b.assert_not_called()
|
||||||
|
|
||||||
|
|
@ -1102,11 +1105,29 @@ class WithNonFields:
|
||||||
create_autospec(WithNonFields(1)),
|
create_autospec(WithNonFields(1)),
|
||||||
]:
|
]:
|
||||||
with self.subTest(mock=mock):
|
with self.subTest(mock=mock):
|
||||||
|
self.assertIsInstance(mock, WithNonFields)
|
||||||
with self.assertRaisesRegex(AttributeError, msg):
|
with self.assertRaisesRegex(AttributeError, msg):
|
||||||
mock.a
|
mock.a
|
||||||
with self.assertRaisesRegex(AttributeError, msg):
|
with self.assertRaisesRegex(AttributeError, msg):
|
||||||
mock.b
|
mock.b
|
||||||
|
|
||||||
|
def test_dataclass_special_attrs(self):
|
||||||
|
@dataclass
|
||||||
|
class Description:
|
||||||
|
name: str
|
||||||
|
|
||||||
|
for mock in [
|
||||||
|
create_autospec(Description, instance=True),
|
||||||
|
create_autospec(Description(1)),
|
||||||
|
]:
|
||||||
|
with self.subTest(mock=mock):
|
||||||
|
self.assertIsInstance(mock, Description)
|
||||||
|
self.assertIs(mock.__class__, Description)
|
||||||
|
self.assertIsInstance(mock.__dataclass_fields__, MagicMock)
|
||||||
|
self.assertIsInstance(mock.__dataclass_params__, MagicMock)
|
||||||
|
self.assertIsInstance(mock.__match_args__, MagicMock)
|
||||||
|
self.assertIsInstance(mock.__hash__, MagicMock)
|
||||||
|
|
||||||
class TestCallList(unittest.TestCase):
|
class TestCallList(unittest.TestCase):
|
||||||
|
|
||||||
def test_args_list_contains_call_list(self):
|
def test_args_list_contains_call_list(self):
|
||||||
|
|
|
||||||
|
|
@ -569,6 +569,11 @@ def _mock_add_spec(self, spec, spec_set, _spec_as_instance=False,
|
||||||
__dict__['_mock_methods'] = spec
|
__dict__['_mock_methods'] = spec
|
||||||
__dict__['_spec_asyncs'] = _spec_asyncs
|
__dict__['_spec_asyncs'] = _spec_asyncs
|
||||||
|
|
||||||
|
def _mock_extend_spec_methods(self, spec_methods):
|
||||||
|
methods = self.__dict__.get('_mock_methods') or []
|
||||||
|
methods.extend(spec_methods)
|
||||||
|
self.__dict__['_mock_methods'] = methods
|
||||||
|
|
||||||
def __get_return_value(self):
|
def __get_return_value(self):
|
||||||
ret = self._mock_return_value
|
ret = self._mock_return_value
|
||||||
if self._mock_delegate is not None:
|
if self._mock_delegate is not None:
|
||||||
|
|
@ -2766,14 +2771,16 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
|
||||||
raise InvalidSpecError(f'Cannot autospec a Mock object. '
|
raise InvalidSpecError(f'Cannot autospec a Mock object. '
|
||||||
f'[object={spec!r}]')
|
f'[object={spec!r}]')
|
||||||
is_async_func = _is_async_func(spec)
|
is_async_func = _is_async_func(spec)
|
||||||
|
_kwargs = {'spec': spec}
|
||||||
|
|
||||||
entries = [(entry, _missing) for entry in dir(spec)]
|
entries = [(entry, _missing) for entry in dir(spec)]
|
||||||
if is_type and instance and is_dataclass(spec):
|
if is_type and instance and is_dataclass(spec):
|
||||||
|
is_dataclass_spec = True
|
||||||
dataclass_fields = fields(spec)
|
dataclass_fields = fields(spec)
|
||||||
entries.extend((f.name, f.type) for f in dataclass_fields)
|
entries.extend((f.name, f.type) for f in dataclass_fields)
|
||||||
_kwargs = {'spec': [f.name for f in dataclass_fields]}
|
dataclass_spec_list = [f.name for f in dataclass_fields]
|
||||||
else:
|
else:
|
||||||
_kwargs = {'spec': spec}
|
is_dataclass_spec = False
|
||||||
|
|
||||||
if spec_set:
|
if spec_set:
|
||||||
_kwargs = {'spec_set': spec}
|
_kwargs = {'spec_set': spec}
|
||||||
|
|
@ -2810,6 +2817,8 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
|
||||||
|
|
||||||
mock = Klass(parent=_parent, _new_parent=_parent, _new_name=_new_name,
|
mock = Klass(parent=_parent, _new_parent=_parent, _new_name=_new_name,
|
||||||
name=_name, **_kwargs)
|
name=_name, **_kwargs)
|
||||||
|
if is_dataclass_spec:
|
||||||
|
mock._mock_extend_spec_methods(dataclass_spec_list)
|
||||||
|
|
||||||
if isinstance(spec, FunctionTypes):
|
if isinstance(spec, FunctionTypes):
|
||||||
# should only happen at the top level because we don't
|
# should only happen at the top level because we don't
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
Fix :class:`unittest.mock.Mock` generation on :func:`dataclasses.dataclass`
|
||||||
|
objects. Now all special attributes are set as it was before :gh:`124429`.
|
||||||
Loading…
Add table
Add a link
Reference in a new issue