Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

create_autospec() doesn't respect configure_mock style kwargs #90848

Closed
marchantjm mannequin opened this issue Feb 9, 2022 · 2 comments
Closed

create_autospec() doesn't respect configure_mock style kwargs #90848

marchantjm mannequin opened this issue Feb 9, 2022 · 2 comments
Labels
3.8 only security fixes 3.9 only security fixes 3.10 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@marchantjm
Copy link
Mannequin

marchantjm mannequin commented Feb 9, 2022

BPO 46690
Nosy @cjw296, @voidspace, @lisroach, @mariocj89, @tirkarthi

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = None
created_at = <Date 2022-02-09.11:54:00.145>
labels = ['3.8', 'type-bug', 'library', '3.9', '3.10']
title = "create_autospec() doesn't respect configure_mock style kwargs"
updated_at = <Date 2022-02-10.13:44:04.417>
user = 'https://bugs.python.org/marchantjm'

bugs.python.org fields:

activity = <Date 2022-02-10.13:44:04.417>
actor = 'xtreak'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Library (Lib)']
creation = <Date 2022-02-09.11:54:00.145>
creator = 'marchant.jm'
dependencies = []
files = []
hgrepos = []
issue_num = 46690
keywords = []
message_count = 2.0
messages = ['412898', '413001']
nosy_count = 6.0
nosy_names = ['cjw296', 'michael.foord', 'lisroach', 'mariocj89', 'xtreak', 'marchant.jm']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = 'behavior'
url = 'https://bugs.python.org/issue46690'
versions = ['Python 3.8', 'Python 3.9', 'Python 3.10']

Linked PRs

@marchantjm
Copy link
Mannequin Author

marchantjm mannequin commented Feb 9, 2022

When using create_autospec() to create a mock object, it doesn't respect values passed through in the style described for passing mock configurations in the Mock constructor (https://docs.python.org/3.8/library/unittest.mock.html#unittest.mock.Mock.configure_mock). Instead, they seem to get discarded somewhere here (

cpython/Lib/unittest/mock.py

Lines 2693 to 2741 in 77bab59

for entry in dir(spec):
if _is_magic(entry):
# MagicMock already does the useful magic methods for us
continue
# XXXX do we need a better way of getting attributes without
# triggering code execution (?) Probably not - we need the actual
# object to mock it so we would rather trigger a property than mock
# the property descriptor. Likewise we want to mock out dynamically
# provided attributes.
# XXXX what about attributes that raise exceptions other than
# AttributeError on being fetched?
# we could be resilient against it, or catch and propagate the
# exception when the attribute is fetched from the mock
try:
original = getattr(spec, entry)
except AttributeError:
continue
kwargs = {'spec': original}
if spec_set:
kwargs = {'spec_set': original}
if not isinstance(original, FunctionTypes):
new = _SpecState(original, spec_set, mock, entry, instance)
mock._mock_children[entry] = new
else:
parent = mock
if isinstance(spec, FunctionTypes):
parent = mock.mock
skipfirst = _must_skip(spec, entry, is_type)
kwargs['_eat_self'] = skipfirst
if iscoroutinefunction(original):
child_klass = AsyncMock
else:
child_klass = MagicMock
new = child_klass(parent=parent, name=entry, _new_name=entry,
_new_parent=parent,
**kwargs)
mock._mock_children[entry] = new
_check_signature(original, new, skipfirst=skipfirst)
# so functions created with _set_signature become instance attributes,
# *plus* their underlying mock exists in _mock_children of the parent
# mock. Adding to _mock_children may be unnecessary where we are also
# setting as an instance attribute?
if isinstance(new, FunctionTypes):
setattr(mock, entry, new)
).

Here's a simple test case:

from unittest.mock import create_autospec

class Test:
    def test_method(self):
        pass

autospec_mock = create_autospec(Test, instance=True, **{"test_method.side_effect": ValueError})

# Should throw a ValueError exception
autospec_mock.test_method()

# Assign manually
autospec_mock.test_method.side_effect = ValueError
# Throws as expected
autospec_mock.test_method()

@marchantjm marchantjm mannequin added tests Tests in the Lib/test dir 3.8 only security fixes 3.9 only security fixes 3.10 only security fixes type-bug An unexpected behavior, bug, or error labels Feb 9, 2022
@tirkarthi
Copy link
Member

I guess the problem is that during the initial mock creation kwargs is passed so calling test_method immediately after mock creation raises ValueError. But as we loop through the attributes and create new child mock for the attributes the original configured mock for the method that raises ValueError is overridden by another object without the configuration info. Probably it makes sense to call configure_mock again after all children mock are constructed. Something like below works and I don't see any test failures in mock related test cases.

index 2719f74d6f..585e875c95 100644
--- a/Lib/unittest/mock.py
+++ b/Lib/unittest/mock.py
@@ -2637,6 +2637,7 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
                                f'[object={spec!r}]')
     is_async_func = _is_async_func(spec)
     _kwargs = {'spec': spec}
+    original_kwargs = kwargs
     if spec_set:
         _kwargs = {'spec_set': spec}
     elif spec is None:
@@ -2740,6 +2741,9 @@ def create_autospec(spec, spec_set=False, instance=False, _parent=None,
         if isinstance(new, FunctionTypes):
             setattr(mock, entry, new)
 
+    if _is_instance_mock(mock):
+        mock.configure_mock(**original_kwargs)
+
     return mock

@tirkarthi tirkarthi added stdlib Python modules in the Lib dir and removed tests Tests in the Lib/test dir labels Feb 10, 2022
@ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
infohash added a commit to infohash/cpython that referenced this issue Apr 22, 2024
infohash added a commit to infohash/cpython that referenced this issue Apr 22, 2024
miss-islington pushed a commit to miss-islington/cpython that referenced this issue May 2, 2024
…wargs (pythonGH-118163)

(cherry picked from commit b28a333)

Co-authored-by: infohash <46137868+infohash@users.noreply.github.com>
@cjw296 cjw296 closed this as completed May 2, 2024
cjw296 pushed a commit that referenced this issue May 2, 2024
…kwargs (GH-118163) (#118517)

gh-90848: Fixed create_autospec ignoring configure_mock style kwargs (GH-118163)
(cherry picked from commit b28a333)

Co-authored-by: infohash <46137868+infohash@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.8 only security fixes 3.9 only security fixes 3.10 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
Projects
Status: Done
Development

No branches or pull requests

2 participants