PROJET AUTOBLOG


Sam & Max: Python, Django, Git et du cul

Site original : Sam & Max: Python, Django, Git et du cul

⇐ retour index

Comment mocker une coroutine ? 6   Recently updated !

lundi 25 janvier 2016 à 11:34

Dans le guide sur les tests en python (que je dois toujours terminer, je sais…), je vous parle des objets mocks.

Si vous avez eu le plaisir de jouer avec asyncio, vous avez du noter que unittest.mock n’a aucun outil pour gérer gérer les coroutines.

En attendant que ce soit intégré à la stdlib, voici une petite recette :

import asyncio
from unittest.mock import Mock
 
# on utilise toute la machinerie du Mock original
class AMock(Mock):
 
 
    def __call__(self, *args, **kwargs):
        # la référence du parent doit se récupérer hors
        # hors de la closure
        parent = super(AMock, self) 
        # sauf qu'à l'appel on créé une fonction coroutine
        @asyncio.coroutine
        def coro():
            # Qui fait le vrai Mock.__call__ (et donc popule l'historique 
            # des appels), mais seulement après l'évent loop l'ait éxécuté
            return parent.__call__(*args, **kwargs)
 
        # On appelle la fonction coroutine pour générer une coroutine
        # (les coroutines marchent comme les générateurs)
        return coro()

Je propose qu’en l’honneur de ce bidouillage, on l’appelle… mockoroutine !

Ca va s’utiliser comme ça:

mockorourine = AMock()
yield from mockorourine()

Après le yield from, mockorourine.call_count == 1, et mockorourine.assert_called_once_with()passe.

Si vous êtes en 3.5+, on peut même faire:

class AMock(Mock):
 
    def __call__(self, *args, **kwargs):
        parent = super(AMock, self)
        async def coro():
            return parent.__call__(*args, **kwargs)
        return coro()
 
    def __await__(self):
        # on delegue le await à la couroutine créée par __call__
        return self().__await__()

Puis:

await AMock()