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

AsyncMock issue with awaitable return_value/side_effect/wraps #83038

Closed
fried mannequin opened this issue Nov 19, 2019 · 6 comments
Closed

AsyncMock issue with awaitable return_value/side_effect/wraps #83038

fried mannequin opened this issue Nov 19, 2019 · 6 comments
Labels
3.8 only security fixes 3.9 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@fried
Copy link
Mannequin

fried mannequin commented Nov 19, 2019

BPO 38857
Nosy @asvetlov, @dimaqq, @fried, @lisroach, @tirkarthi
PRs
  • bpo-38857: AsyncMock fix for awaitable values and StopIteration fix [3.8] #17269
  • [3.8] bpo-38857: AsyncMock fix for awaitable values and StopIteration… (GH-17269) #17304
  • 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 = <Date 2019-11-21.18:17:51.344>
    created_at = <Date 2019-11-19.23:04:13.839>
    labels = ['3.8', 'type-bug', 'library', '3.9']
    title = 'AsyncMock issue with awaitable return_value/side_effect/wraps'
    updated_at = <Date 2020-02-18.00:33:14.353>
    user = 'https://github.com/fried'

    bugs.python.org fields:

    activity = <Date 2020-02-18.00:33:14.353>
    actor = 'Dima.Tisnek'
    assignee = 'none'
    closed = True
    closed_date = <Date 2019-11-21.18:17:51.344>
    closer = 'lisroach'
    components = ['Library (Lib)']
    creation = <Date 2019-11-19.23:04:13.839>
    creator = 'fried'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 38857
    keywords = ['patch']
    message_count = 6.0
    messages = ['357000', '357116', '357190', '362115', '362160', '362165']
    nosy_count = 5.0
    nosy_names = ['asvetlov', 'Dima.Tisnek', 'fried', 'lisroach', 'xtreak']
    pr_nums = ['17269', '17304']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue38857'
    versions = ['Python 3.8', 'Python 3.9']

    @fried
    Copy link
    Mannequin Author

    fried mannequin commented Nov 19, 2019

    If you are trying to use AsyncMock to mock a coroutine that returns awaitable objects, AsyncMock awaits on those objects instead of returning them as is.

    Example:
    mock = AsyncMock(return_value=asyncio.Future())
    v = await mock() # blocks on trying to await the future

    Expected:
    mock = AsyncMock(return_value=asyncio.Future())
    v = await mock()
    assert isisnstance(v, asyncio.Future)

    This problem affects side_effects and wraps.

    @fried fried mannequin added 3.8 only security fixes 3.9 only security fixes stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error labels Nov 19, 2019
    @lisroach
    Copy link
    Contributor

    New changeset 046442d by Lisa Roach (Jason Fried) in branch 'master':
    bpo-38857: AsyncMock fix for awaitable values and StopIteration fix [3.8] (GH-17269)
    046442d

    @asvetlov
    Copy link
    Contributor

    New changeset b2744c1 by Andrew Svetlov (Lisa Roach) in branch '3.8':
    [3.8] bpo-38857: AsyncMock fix for awaitable values and StopIteration fix [3.8] (GH-17269) (bpo-17304)
    b2744c1

    @dimaqq
    Copy link
    Mannequin

    dimaqq mannequin commented Feb 17, 2020

    I think this deserves discussion :)

    On one hand, it's a welcome change, on another it's kind of a regression.

    Up until 3.8, our tests used to look like this:

    ---
    # code under test

    async def foo():
        return await bar()

    # test

    async def helper(value):
        return value
    
    
    async def test_foo():
        with patch("bar", return_value=helper(42)):
            assert await foo() == 42

    I feel that the default class patch() uses for new has crept in too quietly in 3.8.

    At the same time, helper was only used because there was no AsyncMock.
    (or at times, a 3rd party library, asynctest was used).

    So, on one hand, it's a bit of a regression, but on the other, looking ahead, I would really like unittest.mock to do the right thing.

    Can we have it both ways? If not, what way is a better way?

    @fried
    Copy link
    Mannequin Author

    fried mannequin commented Feb 17, 2020

    Its not possible to have it both ways. Also it stinks too much of trying to guess.

    The root of your issue is you want a normal MagicMock not an AsyncMock. Its the automatic behavior of patch to pick AsyncMock vs MagicMock that is the heart of your issue. This bug fix doesn't involve that behavior at all, and AsyncMock was measurably broken without the fix, in an unavoidable way. Your breakage is avoidable by changes to how you patch.

    with patch("bar", return_value=42)

    To still do it the old way you would have to pass new_callable=MagicMock to patch.

    @dimaqq
    Copy link
    Mannequin

    dimaqq mannequin commented Feb 18, 2020

    Thank you for explanation, Jason!

    I guess that the bug report and the patch were too technical for me to understand 😅

    I'm happy with the behaviour in Python 3.8.1 and now I know it's going to stay, I'll just change the tests in our code base.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    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 stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    2 participants