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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With