Puis-je patch un Python décorateur avant qu'il s'enveloppe d'une fonction?
J'ai une fonction avec un décorateur que je suis en train de tester avec l'aide de l'Python Maquette de la bibliothèque. J'aimerais utiliser la maquette.patch pour remplacer le vrai décorateur avec un simulacre de "contournement" décorateur qui se contente d'appeler la fonction. Ce que je ne peux pas savoir comment appliquer le patch avant le vrai décorateur encapsule la fonction. J'ai essayé un peu différentes variations sur le patch de la cible et de la réorganisation de la pièce et les instructions d'importation, mais sans succès. Des idées?
Vous devez vous connecter pour publier un commentaire.
Décorateurs sont appliqués à la définition de la fonction du temps. Pour la plupart des fonctions, c'est quand le module est chargé. (Les fonctions qui sont définies dans d'autres fonctions ont le décorateur appliquée à chaque fois que la fonction englobante est appelé.)
Donc, si vous voulez singe-patch un décorateur, ce que vous devez faire est de:
module.decorator = mymockdecorator
Si le module qui contient le décorateur contient également des fonctions qui l'utilisent, ceux qui sont déjà décorées par le temps, vous pouvez les voir, et vous êtes probablement S. O. L.
Modifier pour refléter les changements de Python depuis l'origine, j'avais écrit ceci: Si le décorateur utilise
functools.wraps()
et la version de Python est assez nouveau, vous pourriez être en mesure de creuser la fonction d'origine à l'aide de la__wrapped__
attritube et re-décorer, mais ce n'est pas garanti, et le décorateur vous souhaitez remplacer peut aussi ne pas être le seul décorateur appliquée.reload
fonction, dans le but de régénérer le python code binaire docs.python.org/2/library/functions.html#reload et monkeypatch votre décorateur__init__
. Qui a assuré que le patch a été chargée avant que le fichier de test. Nous avons isolé des tests de dossier, de sorte que la stratégie fonctionne pour nous, mais cela peut ne pas fonctionner pour chaque dossier de mise en page.Il convient de noter que plusieurs des réponses ici patche le décorateur pour l'ensemble de la session de test plutôt que d'une seule instance de test; ce qui peut être indésirable. Voici comment patcher un décorateur qui persiste uniquement par le biais d'un seul test.
De notre unité à être testé avec les indésirables décorateur:
De décorateurs module:
Par le temps, notre test est recueillie au cours d'une série de tests, les indésirables décorateur a déjà été appliquée à notre unité sous test (parce que ce qui se passe à l'heure d'importation). Afin de se débarrasser de cela, nous aurons besoin de remplacer manuellement le décorateur le décorateur du module, puis ré-importer le module contenant notre UET.
Notre module de test:
Le nettoyage de rappel, kill_patches, restaure l'original décorateur et re-s'applique à l'unité que nous étions l'essai. De cette façon, notre patch persiste uniquement par le biais d'un test unique, plutôt que l'ensemble de la session -- et c'est exactement comme n'importe quel autre patch devrait se comporter. Aussi, depuis le nettoyage des appels patch.stopall(), nous pouvons démarrer tous les autres patchs dans le setUp() nous avons besoin et ils seront nettoyées en un seul endroit.
La chose importante à comprendre au sujet de cette méthode est de savoir comment le rechargement aura un effet. Si un module prend trop de temps ou est la logique qui s'exécute lors de l'importation, vous aurez juste besoin de hausser les épaules et de tester le décorateur dans le cadre de l'unité. 🙁 J'espère que votre code est bien écrit que ça. Droit?
Si l'on ne se soucie pas si le patch est appliqué à l'ensemble de la session de test, la façon la plus simple de le faire est de droite en haut du fichier de test:
Assurez-vous de corriger le fichier avec le décorateur plutôt que de l'étendue locale de l'UET et de lancer le patch avant l'importation de l'unité avec le décorateur.
Fait intéressant, même si le patch est arrêté, tous les fichiers déjà importés auront toujours le patch appliqué à le décorateur, ce qui est l'inverse de la situation, nous avons commencé avec. Sachez que cette méthode de patch tous les autres fichiers dans l'essai qui sont importés par la suite, même s'ils ne déclarent pas un patch eux-mêmes.
Quand j'ai couru à travers ce problème, j'utilise la grille de mon cerveau pendant des heures. J'ai trouvé un moyen beaucoup plus facile de gérer cela.
Ce sera entièrement de contourner le décorateur, à l'instar de la cible n'était même pas décoré en premier lieu.
Celui-ci se décompose en deux parties. Je suggère la lecture de l'article suivant.
http://alexmarandon.com/articles/python_mock_gotchas/
Deux Problèmes que je rencontrais:
1.) Se moquer de l'Décorateur avant l'importation de votre fonction/module.
Les décorateurs et les fonctions sont définies à la fois que le module est chargé.
Si vous n'avez pas de maquette avant l'importation, il ne tiendra pas compte de la maquette. Après le chargement, vous avez à faire un drôle de se moquer.le patch.objet, ce qui est encore plus frustrant.
2.) Assurez-vous de tourner en dérision le chemin d'accès correct à le décorateur.
Rappelez-vous que le patch de la décoratrice vous êtes moqueur est basé sur la façon dont votre module charge le décorateur, pas la façon dont votre test de charge le décorateur. C'est pourquoi je suggère toujours à l'aide de chemins d'accès complets pour les importations. Cela rend les choses beaucoup plus facile pour les tests.
Suit:
1.) La simulation de la fonction:
2.) Se moquer de la décoratrice:
2a.) Chemin à l'intérieur avec.
2b). Patch au dessus de fichier, ou en cas de test.le programme d'installation
L'un de ces moyens vous permettent d'importer votre fonction à tout moment dans le cas de test ou de sa méthode/cas de test.
2.) L'utilisation d'une fonction distincte comme un effet secondaire de la maquette.patch.
Maintenant, vous pouvez utiliser mock_decorator pour un décorateur, vous voulez vous moquer. Vous aurez à se moquer les uns des le décorateur séparément, donc attention à ceux que vous manquez.
La suite fonctionné pour moi:
Il a travaillé comme un charme.
Peut-être vous pouvez appliquer un autre décorateur sur la définition de tous les décorateurs que, fondamentalement, les vérifications de certaines variables de configuration pour voir si le mode d'essai est destinée à être utilisée.
Si oui, il remplace le décorateur c'est décorer avec un mannequin décorateur qui ne fait rien.
Autrement, il permet de ce décorateur à travers.
Concept
Cela peut sembler un peu étrange, mais on peut patch
sys.path
, avec une copie de lui-même, et d'effectuer une importation dans le champ d'application de la fonction de test. Le code suivant illustre le concept.MODULE
peut alors être remplacé par le module de test. (Cela fonctionne en Python 3.6 avecMODULE
substitué avecxml
par exemple)OP
Pour votre cas, disons que la fonction décorateur réside dans le module
pretty
et décoré de la fonction réside danspresent
, alors vous patchpretty.decorator
à l'aide de la simulation de machines et de leur substituerMODULE
avecpresent
. Quelque chose comme ce qui suit devrait fonctionner (non testé).classe TestDecorator(unittest.Cas de test) :
...
Explication
Cela fonctionne en fournissant un "nettoyage" de
sys.path
pour chaque fonction de test, à l'aide d'une copie de lasys.path
du module de test. Cette copie est faite lorsque le module est d'abord analysée assurer la cohérence de l'sys.path
pour tous les tests.Nuances
Il y a quelques implications, cependant. Si le framework de test s'exécute de multiples modules de test dans les mêmes python session de test du module que les importations
MODULE
à l'échelle mondiale sauts de n'importe quel module de test que les importations localement. Cela oblige à effectuer l'importation localement partout. Si le cadre s'exécute à chaque module de test dans le cadre d'un python session alors cela devrait fonctionner. De même, vous ne pouvez pas importer d'MODULE
globalement dans le cadre d'un module de test où vous importezMODULE
localement.Le local des importations doit être effectué pour chaque test de la fonction au sein d'une sous-classe de
unittest.TestCase
. C'est peut-être possible de l'appliquer à launittest.TestCase
sous-classe de faire une importation particulière du module disponible pour toutes les fonctions de test au sein de la classe.Construit Ins
Ceux déconner avec
builtin
importations trouverez remplacementMODULE
avecsys
,os
etc. va échouer, car ce sont des alread sursys.path
lorsque vous essayez de le copier. L'astuce ici est de lancer Python avec le groupe builtin importations désactivé, je pense quepython -X test.py
va le faire mais j'ai oublié le drapeau approprié (Voirpython --help
). Ceux-ci peuvent ensuite être importées localement à l'aide deimport builtins
, IIRC.pour @lru_cache(max_size=1000)