Se moquant des méthodes sur toute instance d'une classe python
Je veux en dérision les méthodes sur toute instance d'une classe dans le code de production afin de faciliter les tests. Est-il une bibliothèque Python qui pourrait faciliter ce processus?
En gros, je veux faire la suite, mais en Python (le code suivant est Ruby, à l'aide de la Moka bibliothèque):
def test_stubbing_an_instance_method_on_all_instances_of_a_class
Product.any_instance.stubs(:name).returns('stubbed_name')
assert_equal 'stubbed_name', SomeClassThatUsesProduct.get_new_product_name
end
La chose importante à noter à partir de ci-dessus, c'est que j'ai besoin de le simuler sur le niveau de la classe, depuis que je suis réellement besoin de se moquer des méthodes sur une instance créée par la chose que je suis en essais.
Cas D'Utilisation:
J'ai une classe QueryMaker
qui appelle une méthode sur une instance de RemoteAPI
. Je veux, à se moquer de la RemoteAPI.get_data_from_remote_server
méthode retourne une constante. Comment puis-je le faire à l'intérieur d'un test sans avoir à mettre un cas particulier au sein de la RemoteAPI
code pour vérifier que l'environnement de fonctionnement.
Exemple de ce que je voulais en action:
# a.py
class A(object):
def foo(self):
return "A's foo"
# b.py
from a import A
class B(object):
def bar(self):
x = A()
return x.foo()
# test.py
from a import A
from b import B
def new_foo(self):
return "New foo"
A.foo = new_foo
y = B()
if y.bar() == "New foo":
print "Success!"
OriginalL'auteur Jamie Wong | 2011-02-18
Vous devez vous connecter pour publier un commentaire.
Façon la plus simple est probablement d'utiliser une méthode de classe. Vous devriez vraiment utiliser une méthode d'instance, mais c'est une douleur à ceux qui créent, alors qu'il y a une fonction intégrée qui crée une méthode de classe. Avec une méthode de classe, votre talon obtiendrez une référence à la classe (plutôt que de l'instance) comme premier argument, mais comme c'est un stub c'est probablement ce qui n'a pas d'importance. Donc:
Noter que la signature de la lambda doit correspondre à la signature de la méthode que vous remplacez. Aussi, bien sûr, depuis le Python (comme Ruby) est un langage dynamique, il n'y a aucune garantie que quelqu'un ne commute pas votre écrasa méthode pour quelque chose d'autre avant que vous obtenez vos mains sur l'instance, même si je pense que vous saurez très vite si cela arrive.
Edit: Sur la poursuite de l'enquête, vous pouvez laisser de côté la
classmethod()
:J'essayais de conserver l'original comportement de la méthode d'aussi près que possible, mais il semble que ce n'est pas vraiment nécessaire (et ne préserve pas le comportement que je l'avais espéré, de toute façon).
Il se doit; votre cas d'utilisation est à peu près le même que celui de votre exemple. Au fond, par la modification de la définition de la méthode de la classe, vous sont en train de changer sa définition sur toutes les instances. Le
classmethod
fonction permet de créer un wrapper qui s'avère essentiel de la fonction dans une méthode, et lalambda
définit la fonction pour simplement retourner une chaîne statique.... mais voir mon edit.
Bien, j'ai de l'expérience avec cela, mais je suis un peu voilé sur exactement comment les choses se mettre en place lorsque vous importez. Si je l'importation
RemoteAPI
et remplacer ensuite l'une des méthodes, puisQueryMaker
importationsRemoteAPI
au cours de son exécution normale, la version deRemoteAPI
estQueryMaker
à l'aide? Ma version ou la version d'origine?Notez également que toutes les instances de
Product
créé avant de redéfinirProduct.name
sera également "mis à jour" pour la nouvelle méthode, ce qui est probablement ce que vous voulez de toute façon. En outre, cette méthode ne sera pas conserver l'original en fonction du nom et de la docstring.OriginalL'auteur
Avoir à se moquer de méthodes lors de l'essai est très commun et il ya beaucoup d'outils pour vous aider avec elle en Python. Le danger avec "monkey patching" classes comme cela, c'est que si vous n'avez pas annuler c'est ensuite la classe a été modifiée pour tous les autres usages tout au long de vos tests.
Ma bibliothèque se moquer, qui est l'un des plus populaires Python se moquant de bibliothèques, comprend une aide appelée "patch" qui vous permet en toute sécurité de patch de méthodes ou d'attributs sur les objets et les classes lors de vos tests.
La maquette du module est disponible à partir de:
http://pypi.python.org/pypi/mock
Le patch décorateur peut être utilisé comme un gestionnaire de contexte ou comme un test de décorateur. Vous pouvez soit l'utiliser pour patch avec des fonctions vous-même, ou l'utiliser pour automatiquement patch avec des objets Fantaisie qui sont très configurable.
Cela gère la unpatching automatiquement pour vous. Vous pourriez vous en sortir sans la définition de la simulation de la fonction de vous-même:
mock
, mais je ne vois pas les exemples les plus évidents où il a été utilisé pour corriger les choses qui se sont importés d'ailleurs. +1Juste être prudent avec direct monkey patching dans vos tests, il est facilement casser des choses. se moquer.le patch peut être utilisé pour réparer des choses dans tout l'espace de noms, soit des objets dans l'espace de noms courant (avec
patch.object
) ou dans un autre espace de noms spécifié par un nom:patch('mymodule.myclass')
.La chose importante à savoir est que si vous êtes patcher par un nom puis vous patch le nom du module, c'est utilisé, pas dans le module il est défini. par exemple, si je voulais patch de l'utilisation de
SomeClass
dans le modulefoo
, même siSomeClass
est définie dans le modulebar
mais est importé dansfoo
, puis ce que vous patch estfoo.SomeClass
et pasbar.SomeClass
. Pour patcher une méthode de classe, il n'a pas d'importance tant que la classe sera le même objet dans les deux modules. Pour d'autres choses, il importe beaucoup.Ne devrait pas le cas de test appeler Y. foo() == "New foo" ?? C'est l'exemple de la simulation de la documentation pour le patch.objet: @patch.objet(SomeClass, 'class_method')
C'est le plus déroutant exemple de code que j'ai jamais rencontré. Il n'y a aucun moyen que B == A et foo == bar. Mais les deux sont utilisés de manière interchangeable dans le présent exemple.
OriginalL'auteur
Je ne sais pas Ruby assez bien assez pour dire exactement ce que vous essayez de faire, mais de vérifier la
__getattr__
méthode. Si vous définissez dans votre classe, Python va appeler lorsque le code tente d'accéder à un attribut de ta classe qui n'est pas autrement définie. Puisque vous voulez qu'il soit une méthode, il sera nécessaire de créer une méthode à la volée qu'il renvoie.Également noter que
__getattr__
doit pas utiliser n'importe quel autre pas défini les attributs de ta classe, ou sinon, il sera infiniment récursive, appelant__getattr__
pour l'attribut qui n'existe pas.Si c'est quelque chose que vous ne voulez faire de votre code de test, pas le code de production, puis de mettre votre classe normale définition dans le code de production du fichier, puis dans le code de test définir la
__getattr__
méthode (non relié), puis le lier à la classe que vous souhaitez:Je ne suis pas sûr de savoir comment cela réagit avec une classe qui a déjà été à l'aide de
__getattribute__
(par opposition à__getattr__
,__getattribute__
est appelée pour tous les attributs de savoir si ou non ils existent).Si vous voulez seulement faire pour des méthodes spécifiques qui existent déjà, alors vous pourriez faire quelque chose comme:
Ou si vous avez vraiment envie d'être élégant, vous pouvez créer une fonction wrapper pour faire de la réalisation de plusieurs méthodes faciles:
Si c'est ce que vous voulez faire, je suis sûr que vous pouvez le définir comme un indépendant de la fonction dans votre code de test, puis le lier à la classe à partir de votre code de test via
Product.__getattr__ = fn_name
Je comprends ce que
__getattr__
est utilisé pour, et je ne comprends vraiment pas pourquoi vous pensez que ce serait utile. J'ai besoin de remplacer une méthode qui est déjà définie sur la classe-je besoin pour se moquer de la méthode.Vérifiez les deux derniers exemples dans ma réponse, le modifier montre comment remplacer des méthodes spécifiques. Comme je l'ai dit, je ne sais pas vraiment Ruby, de sorte que lorsque vous l'avez mentionné, se moquant des méthodes et de la création d'un bouchon, j'ai pensé que vous étiez en train de créer des bouts de test pour toutes vos méthodes pour faciliter l'écriture de tests unitaires. Je vois maintenant que c'est une méthode spécifique que vous souhaitez remplacer, de sorte que les deux derniers exemples montrent comment le faire.
OriginalL'auteur
Simulacre est la façon de le faire, d'accord.
Il peut être un peu délicat à assurez-vous que vous êtes patcher la méthode d'instance sur toutes les instances créées à partir de la classe.
Référencé dans la docs, para départ "Pour configurer les valeurs de retour sur les méthodes d'instances sur le patché classe..."
OriginalL'auteur