classification
Title: AsyncMock issue with awaitable return_value/side_effect/wraps
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.9, Python 3.8
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: Dima.Tisnek, asvetlov, fried, lisroach, xtreak
Priority: normal Keywords: patch

Created on 2019-11-19 23:04 by fried, last changed 2020-02-18 00:33 by Dima.Tisnek. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 17269 merged fried, 2019-11-20 00:38
PR 17304 merged lisroach, 2019-11-21 00:38
Messages (6)
msg357000 - (view) Author: Jason Fried (fried) * Date: 2019-11-19 23:04
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.
msg357116 - (view) Author: Lisa Roach (lisroach) * (Python committer) Date: 2019-11-21 00:27
New changeset 046442d02bcc6e848e71e93e47f6cde9e279e993 by Lisa Roach (Jason Fried) in branch 'master':
bpo-38857: AsyncMock fix for awaitable values and StopIteration fix [3.8] (GH-17269)
https://github.com/python/cpython/commit/046442d02bcc6e848e71e93e47f6cde9e279e993
msg357190 - (view) Author: Andrew Svetlov (asvetlov) * (Python committer) Date: 2019-11-21 18:14
New changeset b2744c1be73f5af0d2dc4b952389efc90c8de94e 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) (#17304)
https://github.com/python/cpython/commit/b2744c1be73f5af0d2dc4b952389efc90c8de94e
msg362115 - (view) Author: Dima Tisnek (Dima.Tisnek) * Date: 2020-02-17 02:57
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?
msg362160 - (view) Author: Jason Fried (fried) * Date: 2020-02-17 22:59
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.
msg362165 - (view) Author: Dima Tisnek (Dima.Tisnek) * Date: 2020-02-18 00:33
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.
History
Date User Action Args
2020-02-18 00:33:14Dima.Tisneksetmessages: + msg362165
2020-02-17 22:59:03friedsetmessages: + msg362160
2020-02-17 03:27:53xtreaksetnosy: + xtreak
2020-02-17 02:57:16Dima.Tisneksetnosy: + Dima.Tisnek
messages: + msg362115
2019-11-21 18:17:51lisroachsetstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2019-11-21 18:14:36asvetlovsetnosy: + asvetlov
messages: + msg357190
2019-11-21 00:38:41lisroachsetpull_requests: + pull_request16796
2019-11-21 00:27:56lisroachsetmessages: + msg357116
2019-11-20 00:38:18friedsetkeywords: + patch
stage: patch review
pull_requests: + pull_request16761
2019-11-20 00:14:48friedsettype: behavior
2019-11-19 23:04:13friedcreate