web-dev-qa-db-fra.com

Python - l'objet MagicMock ne peut pas être utilisé dans l'expression 'wait')

Lorsque j'essayais de simuler une fonction asynchrone en unittest avec MagicMock, j'ai eu cette exception:

TypeError: l'objet MagicMock ne peut pas être utilisé dans l'expression 'wait'

Avec un exemple de code comme:

# source code
class Service:
    async def compute(self, x):
        return x

class App:
    def __init__(self):
        self.service = Service()

    async def handle(self, x):
        return await self.service.compute(x)

# test code
import asyncio
import unittest
from unittest.mock import patch


class TestApp(unittest.TestCase):
    @patch('__main__.Service')
    def test_handle(self, mock):
        loop = asyncio.get_event_loop()
        app = App()
        res = loop.run_until_complete(app.handle('foo'))
        app.service.compute.assert_called_with("foo")

if __name__ == '__main__':
    unittest.main()

Comment dois-je le réparer avec les bibliothèques python3 intégrées?

11
shaun shia

Je me suis retrouvé avec ce hack.

# monkey patch MagicMock
async def async_magic():
    pass

MagicMock.__await__ = lambda x: async_magic().__await__()

Cela ne fonctionne que pour MagicMock, pas pour les autres valeurs de retour prédéfinies

12
shaun shia

Vous pouvez obtenir des simulations pour renvoyer des objets qui peuvent être attendus en utilisant un Future . Ce qui suit est un cas de test pytest , mais quelque chose de similaire devrait être possible avec nittest .

async def test_that_mock_can_be_awaited():
    mock = MagicMock(return_value=Future())
    mock.return_value.set_result(123)
    result = await mock()
    assert result == 123

Dans votre cas, puisque vous patchez Service (qui est passé sous la forme mock), mock.return_value = Future() devrait faire l'affaire.

8
z0r

shaun shia a fourni une très bonne solution universelle, mais j'ai trouvé ce que dans python 3.8 vous pouvez utiliser juste @patch('__main__.Service', new=AsyncMock)

1
mani