J'essaie de Mock une fonction (qui retourne du contenu externe) en utilisant le module python mock (http://www.voidspace.org.uk/python/mock/index.html).
J'ai des problèmes pour se moquer des fonctions qui sont importées dans un module.
Par exemple, dans util.py j'ai
def get_content():
return "stuff"
Je veux me moquer d'util.get_content pour qu'il renvoie autre chose.
J'essaye ceci:
util.get_content=Mock(return_value="mocked stuff")
Si get_content
est invoqué dans un autre module, il ne semble jamais réellement renvoyer l'objet simulé. Suis-je en train de manquer quelque chose sur la façon d'utiliser Mock?
Notez que si j'invoque ce qui suit, les choses fonctionnent correctement:
>>> util.get_content=Mock(return_value="mocked stuff")
>>> util.get_content()
"mocked stuff"
Cependant, si get_content est appelé à l'intérieur d'un autre module, il appelle la fonction d'origine au lieu de la version simulée:
>>> from mymodule import MyObj
>>> util.get_content=Mock(return_value="mocked stuff")
>>> m=MyObj()
>>> m.func()
"stuff"
Contenu de mymodule.py
from util import get_content
class MyObj:
def func():
get_content()
Donc, je suppose que ma question est - comment puis-je invoquer la version Mocked d'une fonction à l'intérieur d'un module que j'appelle?
Il semble que le from module import function
peut être à blâmer ici, car il ne pointe pas vers la fonction Mocked.
Je pense avoir une solution de contournement, bien que je ne sois pas encore très clair sur la façon de résoudre le cas général
Dans mon module, si je remplace
from util import get_content
class MyObj:
def func():
get_content()
avec
import util
class MyObj:
def func():
util.get_content()
Le Mock semble être invoqué. Il semble que les espaces de noms doivent correspondre (ce qui est logique). Cependant, la chose étrange est que je m'attendrais
import mymodule
mymodule.get_content = mock.Mock(return_value="mocked stuff")
pour faire l'affaire dans le cas d'origine où j'utilise la syntaxe from/import (qui tire maintenant get_content dans mymodule). Mais cela fait toujours référence au get_content non moqué.
Il s'avère que l'espace de noms est important - il suffit de garder cela à l'esprit lors de l'écriture de votre code.
Vous devez patcher la fonction là où elle est utilisée. Dans votre cas, ce serait dans le module mymodule.
import mymodule
>>> mymodule.get_content = Mock(return_value="mocked stuff")
>>> m = mymodule.MyObj()
>>> m.func()
"mocked stuff"
Il y a une référence dans les documents ici: http://docs.python.org/dev/library/unittest.mock.html#where-to-patch
Supposons que vous créez votre maquette à l'intérieur du module foobar
:
import util, mock
util.get_content = mock.Mock(return_value="mocked stuff")
Si vous importez mymodule
et appelez util.get_content
sans importer au préalable foobar
, votre maquette ne sera pas installée:
import util
def func()
print util.get_content()
func()
"stuff"
Au lieu:
import util
import foobar # substitutes the mock
def func():
print util.get_content()
func()
"mocked stuff"
Notez que foobar
peut être importé de n'importe où (le module A importe B qui importe foobar) tant que foobar
est évalué avant util.get_content
est appelé.
Le cas général serait d'utiliser patch
de mock
. Considérer ce qui suit:
tils.py
def get_content():
return 'stuff'
mymodule.py
from util import get_content
class MyClass(object):
def func(self):
return get_content()
test.py
import unittest
from mock import patch
from mymodule import MyClass
class Test(unittest.TestCase):
@patch('mymodule.get_content')
def test_func(self, get_content_mock):
get_content_mock.return_value = 'mocked stuff'
my_class = MyClass()
self.assertEqual(my_class.func(), 'mocked stuff')
self.assertEqual(get_content_mock.call_count, 1)
get_content_mock.assert_called_once()
Notez comment get_content
est moqué, ce n'est pas util.get_content
, plutôt mymodule.get_content
puisque nous l'utilisons dans mymodule
.
Ci-dessus a été testé avec mock v2.0.0, nosetests v1.3.7 et python v2.7.9.
Bien qu'il ne fournisse pas de réponse directe à votre question, une autre alternative possible consiste à transformer votre fonction en une méthode statique à l'aide de @staticmethod.
Vous pouvez donc transformer vos modules utils en une classe en utilisant quelque chose comme:
class util(object):
@staticmethod
def get_content():
return "stuff"
Ensuite, moquez-le correctement.