How to patch an asynchronous class method?

Val Melev

I'm working on the following problem, I have a class, an asynchronous method of which I want to mock patch:

class ExampleClass:
    async def asy_method(self, param):
        return await some_coroutine(self, param)

example_instance = ExampleClass()

I want to patch specifically only calls like

await example_instance.asy_method('test_param')

Normally I'd use

mocker.patch('ExampleClass.asy_method', new_callable=AsyncMock)

where mocker is the pytest-mock plugin fixture and AsyncMock has the form

class AsyncMock(mock.MagicMock):
    async def __call__(self, *args, **kwargs):
         return super(AsyncMock, self).__call__(*args, **kwargs)

which would give me a Mock object that behaves like a coroutine on call. The problem is, that I want to have access to the self attribute that is passed to the method. self is only passed to the mock object if you set autospec=True though (see also Python Doc on patching unbound methods), which you can't use together with new_callable.

Does anyone have an idea how to resolve this?

Martijn Pieters

Indeed, you can't mix autospeccing and a new callable. Instead, autospec the method, but then replace the side_effect attribute, giving it an AsyncMock() instance:

from unittest import mock


def configure_coroutine_mock(mock_function, klass=AsyncMock):
    """Make an autospecced async function return a coroutine mock"""
    mock_function.side_effect = AsyncMock()
    # mark the side effect as a child of the original mock object
    # so transitive access is recorded on the parent mock too. This is 
    # what .return_value does normally
    mock._check_and_set_parent(
        mock_function.mock, mock_function.side_effect,
        None, '()')
    return mock_asy_method.side_effect


with mocker.patch('ExampleClass.asy_method', autospec=True) as mock_asy_method:
    configure_coroutine_mock(mock_asy_method)

Because the AsyncMock() is a callable object, it'll be called every time mock_asy_method is called, and the arguments are passed on to the object. The result of that call is then used to return from mock_asy_method():

>>> from unittest import mock
>>> class ExampleClass:
...     async def asy_method(self, param):
...         return await some_coroutine(self, param)
...
>>> example_instance = ExampleClass()
>>> with mock.patch('__main__.ExampleClass.asy_method', autospec=True) as mock_asy_method:
...     configure_coroutine_mock(mock_asy_method)
...     print(example_instance.asy_method('foo'))  # call to patched class coroutine
...     print(mock_asy_method.mock_calls)          # calls are recorded
...
<AsyncMock name='asy_method()' id='4563887496'>
<coroutine object AsyncMock.__call__ at 0x1100780f8>
[call(<__main__.ExampleClass object at 0x10ffac1d0>, 'foo')]

As you can see, the self argument and the parameter are recorded in the call, because mock_asy_method is a properly specced function.

Of course, only if the returned AsyncMock() call result is actually awaited will we see that call recorded too:

>>> with mock.patch('__main__.ExampleClass.asy_method', autospec=True) as mock_asy_method:
...     configure_coroutine_mock(mock_asy_method)
...     loop = asyncio.get_event_loop()
...     coro = example_instance.asy_method('foo')
...     loop.run_until_complete(coro)
...     print(mock_asy_method.mock_calls)
...     
<AsyncMock name='asy_method()' id='4564408920'>
<AsyncMock name='asy_method()()' id='4564999360'>    
[call(<__main__.ExampleClass object at 0x10ffac1d0>, 'foo'),
 call()(<__main__.ExampleClass object at 0x10ffac1d0>, 'foo')]

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

How to patch a module method that is called within a class?

From Dev

Python: how to monkey patch class method to other class method

From Dev

Override the class patch with method patch (decorator)

From Dev

Patch a method outside python class

From Java

How to wait for an asynchronous method

From Dev

How to patch/mock a method in Swift?

From Java

How to use PATCH method in CXF

From Dev

Cannot Seem to Patch Class & Method In Another File

From Dev

Is it possible to patch a method that is invoked in another class?

From Dev

How to mock/patch whole class?

From Dev

How to return MagicMock object from a method of a class that was mocked using patch in python

From Dev

Update TextBox from second class with asynchronous method

From Dev

Asynchronous method in a C# class that executes a process

From Dev

How to call a synchronous method from an asynchronous method

From Dev

Monkey patch a python instance method using the original method from the class

From Dev

Python Patch/Mock class method but still call original method

From Dev

how to use PATCH method with Jersey Invocation Builder?

From Dev

How to construct the payload for Web API PATCH Method?

From Dev

How to patch a method decorated with `flask - route` in testing?

From Dev

how to monkey patch the puts method in ruby

From Dev

How handle PATCH method in Flask route as API?

From Dev

How patch multiple method of same object

From Dev

How to get status code for PATCH method in angular?

From Dev

How to monkey patch a `__call__` method?

From Dev

How to dynamically adjust the content of the PATCH method?

From Dev

How to patch and verify if a Class was instantiated with a specific parameter?

From Dev

How to properly monkey patch the Numeric class on RoR?

From Dev

How to mock a class in an instance attribute using patch

From Dev

Mock and patch class method and give its return value to another function

Related Related

  1. 1

    How to patch a module method that is called within a class?

  2. 2

    Python: how to monkey patch class method to other class method

  3. 3

    Override the class patch with method patch (decorator)

  4. 4

    Patch a method outside python class

  5. 5

    How to wait for an asynchronous method

  6. 6

    How to patch/mock a method in Swift?

  7. 7

    How to use PATCH method in CXF

  8. 8

    Cannot Seem to Patch Class & Method In Another File

  9. 9

    Is it possible to patch a method that is invoked in another class?

  10. 10

    How to mock/patch whole class?

  11. 11

    How to return MagicMock object from a method of a class that was mocked using patch in python

  12. 12

    Update TextBox from second class with asynchronous method

  13. 13

    Asynchronous method in a C# class that executes a process

  14. 14

    How to call a synchronous method from an asynchronous method

  15. 15

    Monkey patch a python instance method using the original method from the class

  16. 16

    Python Patch/Mock class method but still call original method

  17. 17

    how to use PATCH method with Jersey Invocation Builder?

  18. 18

    How to construct the payload for Web API PATCH Method?

  19. 19

    How to patch a method decorated with `flask - route` in testing?

  20. 20

    how to monkey patch the puts method in ruby

  21. 21

    How handle PATCH method in Flask route as API?

  22. 22

    How patch multiple method of same object

  23. 23

    How to get status code for PATCH method in angular?

  24. 24

    How to monkey patch a `__call__` method?

  25. 25

    How to dynamically adjust the content of the PATCH method?

  26. 26

    How to patch and verify if a Class was instantiated with a specific parameter?

  27. 27

    How to properly monkey patch the Numeric class on RoR?

  28. 28

    How to mock a class in an instance attribute using patch

  29. 29

    Mock and patch class method and give its return value to another function

HotTag

Archive