Issue
The below example class has a property bar
that is awaitable, as in async_main()
because it (theoretically) does some IO work before returning the answer to everything.
class Foo:
@property
async def bar(self):
return 42
async def async_main():
f = Foo()
print(await f.bar)
I'm having trouble testing it, as the usual suspects of Mock, MagicMock, and AsyncMock don't work with properties as expected. My current workaround is:
f.bar = some_awaitable()
since this makes f.bar a 'field' that can be awaited, but unfortunately I need to access it multiple times while it's under test, which yields RuntimeError: cannot reuse already awaited coroutine
on the second access of course.
Is there an established way to mock an async property like this?
Solution
The easiest way that I can think of is to patch bar
again with an async
property for the purposes of your test.
I am assuming you have some other method on Foo
that you want to test, and that method calls its bar
.
code.py
from asyncio import run
class Foo:
@property
async def bar(self) -> int:
return 42
async def func(self) -> int:
return await self.bar
async def main():
f = Foo()
print(await f.func())
if __name__ == '__main__':
run(main())
test.py
from unittest import IsolatedAsyncioTestCase
from unittest.mock import patch
from . import code
class FooTestCase(IsolatedAsyncioTestCase):
async def test_func(self) -> None:
expected_output = 69420
@property
async def mock_bar(_foo_self: code.Foo) -> int:
return expected_output
with patch.object(code.Foo, "bar", new=mock_bar):
f = code.Foo()
# Just to see that our mocking worked:
self.assertEqual(expected_output, await f.bar)
# Should call `bar` property again:
output = await f.func()
self.assertEqual(expected_output, output)
References: patch
docs.
Answered By - Daniil Fajnberg
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.