Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking an Async Property in Python

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?

like image 747
Brendano257 Avatar asked Sep 11 '25 14:09

Brendano257


1 Answers

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.

like image 59
Daniil Fajnberg Avatar answered Sep 14 '25 04:09

Daniil Fajnberg