Skip to content

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

Closed

Description

@marchantjm
mannequin
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

Activity

  1. marchantjm commented on Feb 9, 2022

    marchantjmmannequin
    MannequinAuthor

    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()
    
  2. tirkarthi commented on Feb 10, 2022

    @tirkarthi
    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
  3. added
    stdlibStandard Library Python modules in the Lib/ directory
    and removed
    testsTests in the Lib/test dir
    on Feb 10, 2022
  4. transferred this issue fromon Apr 10, 2022
  5. added 2 commits that reference this issue on Apr 22, 2024
  6. added a commit that references this issue on May 2, 2024
  7. added a commit that references this issue on May 2, 2024
  8. added a commit that references this issue on May 2, 2024
  9. added a commit that references this issue on May 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.10 (EOL)end of life3.8 (EOL)end of life3.9 (EOL)end of lifestdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions