Quelqu'un peut-il me dire pourquoi cela ne fonctionne pas?
>>> import mock
>>> @mock.patch('datetime.date.today')
... def today(cls):
... return date(2010, 1, 1)
...
>>> from datetime import date
>>> date.today()
datetime.date(2010, 12, 19)
Peut-être que quelqu'un pourrait suggérer une meilleure solution?
mock
bibliothèque: voidspace.org.uk/python/mock/examples.html#partial-mockingRéponses:
Il y a quelques problèmes.
Tout d'abord, la façon dont vous utilisez
mock.patch
n'est pas tout à fait correcte. Lorsqu'il est utilisé comme décorateur, il remplace la fonction / classe donnée (dans ce cas,datetime.date.today
) par unMock
objet uniquement dans la fonction décorée . Donc, seulement dans votretoday()
volonté ildatetime.date.today
y aura une fonction différente, qui ne semble pas être ce que vous voulez.Ce que vous voulez vraiment ressemble plus à ceci:
Malheureusement, cela ne fonctionnera pas:
Cela échoue car les types intégrés Python sont immuables - voir cette réponse pour plus de détails.
Dans ce cas, je sous-classerais moi-même datetime.date et créerais la bonne fonction:
Et maintenant vous pouvez faire:
la source
datetime
instance à sa valeur d'origine? avecdeepcoppy
?patch('mymodule.datetime', Mock(today=lambda: date(2017, 11, 29)))
@patch('module_you_want_to_test.date', Mock( today=Mock(return_value=datetime.date(2017, 11, 29))))
.Une autre option consiste à utiliser https://github.com/spulec/freezegun/
Installez-le:
Et utilisez-le:
Cela affecte également les autres appels datetime dans les appels de méthode à partir d'autres modules:
other_module.py:
main.py:
Et enfin:
la source
Pour ce que ça vaut, les documents Mock parlent spécifiquement de datetime.date.today, et il est possible de le faire sans avoir à créer une classe factice:
https://docs.python.org/3/library/unittest.mock-examples.html#partial-mocking
la source
from datetime import date
alors c'est le nom du module oùfrom datetime import date
et l'appel àdate.today()
apparaîtJe suppose que je suis arrivé un peu en retard pour cela, mais je pense que le problème principal ici est que vous corrigez directement datetime.date.today et, selon la documentation, c'est faux.
Vous devez patcher la référence importée dans le fichier où se trouve la fonction testée, par exemple.
Supposons que vous ayez un fichier functions.py contenant les éléments suivants:
alors, dans votre test, vous devriez avoir quelque chose comme ça
J'espère que cela aide un peu.
la source
NameError: name 'datetime' is not defined
). D'où vient ladatetime.strptime
référenceMock(return_value=...)
si vous n'importez pasdatetime
dans votre fichier de test? MISE À JOUR: C'est OK, j'ai juste continué et j'ai importé ledatetime
module dans le fichier de test. Je pensais que le truc était de savoir comment cacher ladatetime
référence du fichier de test.import datetime
oufrom datetime import strptime
? si vous faisiez le premier, vous auriez à vous moquerdatetime
et à fairemocked_datetime.strptime.return_value = whatever
, est le plus tard, vous devriez vous moquer directement de la référence strptime dans le fichier où réside la méthode testée.Mock(return_value=datetime...)
fonctionne.Pour ajouter à la solution de Daniel G :
Cela crée une classe qui, une fois instanciée, retournera un objet datetime.date normal, mais qui peut également être modifiée.
la source
date
/datetime
lui - même, il utilise la variable disponible globalement, il ne devrait donc pas y avoir de problème: dpaste.com/790310J'ai fait face à la même situation il y a quelques jours, et ma solution était de définir une fonction dans le module pour tester et simplement se moquer de cela:
Aujourd'hui, j'ai découvert FreezeGun , et il semble couvrir magnifiquement ce cas
la source
Le moyen le plus simple pour moi est de faire ceci:
ATTENTION pour cette solution: toutes les fonctionnalités de
datetime module
detarget_module
cesseront de fonctionner.la source
datetime_mock.now = Mock(return_value=datetime(1999, 1, 1)
pourrait même être raccourciedatetime_mock.now.return_value = datetime(1999, 1, 1)
. Au lieu de démarrer le correctif avecstart()
, envisagez d'utiliser lewith patch(...):
gestionnaire de contexte pour vous assurer qu'ildatetime
se comporte à nouveau régulièrement (non simulé) à la fin de votre test.datetime.datetime.now()
défaire ^^?datetime module
dutarget_module
cessera de fonctionner.Vous pouvez utiliser l'approche suivante, basée sur la solution Daniel G. Celui-ci a l'avantage de ne pas casser la vérification de type avec
isinstance(d, datetime.date)
.Fondamentalement, nous remplaçons la
datetime.date
classe basée sur C par notre propre sous-classe python, qui produit desdatetime.date
instances originales et répond auxisinstance()
requêtes exactement comme natifdatetime.date
.Utilisez-le comme gestionnaire de contexte dans vos tests:
Une approche similaire peut être utilisée pour simuler la
datetime.datetime.now()
fonction.la source
__instancecheck__
méthode.De manière générale, vous auriez
datetime
ou peut-êtredatetime.date
importé dans un module quelque part. Un moyen plus efficace de se moquer de la méthode serait de la patcher sur le module qui l'importe. Exemple:a.py
Ensuite, pour votre test, l'objet fictif lui-même serait passé en argument à la méthode de test. Vous devez configurer la maquette avec la valeur de résultat souhaitée, puis appeler votre méthode sous test. Ensuite, vous affirmeriez que votre méthode a fait ce que vous voulez.
Un mot d'avertissement. Il est très certainement possible d’exagérer en se moquant. Lorsque vous le faites, vos tests sont plus longs, plus difficiles à comprendre et impossibles à maintenir. Avant de vous moquer d'une méthode aussi simple que cela
datetime.date.today
, demandez-vous si vous avez vraiment besoin de vous en moquer. Si votre test est court et précis et fonctionne correctement sans se moquer de la fonction, vous pouvez simplement regarder un détail interne du code que vous testez plutôt qu'un objet dont vous devez vous moquer.la source
Voici une autre façon de se moquer
datetime.date.today()
avec un bonus supplémentaire du fait que le reste desdatetime
fonctions continue de fonctionner, car l'objet fictif est configuré pour envelopper ledatetime
module d' origine :Notez l'
wraps=datetime
argument àmock.patch()
- lorsque lefoo_module
utilise d'autresdatetime
fonctions en plus,date.today()
elles seront transmises audatetime
module encapsulé d'origine .la source
Plusieurs solutions sont décrites dans http://blog.xelnor.net/python-mocking-datetime/ . En résumé:
Objet simulé - Simple et efficace mais interrompt les vérifications isinstance ():
Classe simulée
Utilisé comme:
la source
Peut-être pourriez-vous utiliser votre propre méthode "today ()" que vous corrigerez si nécessaire. Un exemple avec utcnow () moqueur peut être trouvé ici: https://bitbucket.org/k_bx/blog/src/tip/source/en_posts/2012-07-13-double-call-hack.rst?at=default
la source
J'ai implémenté la méthode @ user3016183 en utilisant un décorateur personnalisé:
J'ai pensé que ça pourrait aider quelqu'un un jour ...
la source
Il est possible de simuler des fonctions du
datetime
module sans ajouterside_effects
la source
Pour ceux d'entre vous qui utilisent pytest avec moqueur, voici comment je me suis moqué,
datetime.datetime.now()
ce qui est très similaire à la question initiale.Essentiellement, la maquette doit être définie pour renvoyer la date spécifiée. Vous ne pouvez pas patcher directement sur l'objet de datetime.
la source
J'ai fait ce travail en important
datetime
commerealdatetime
et remplacer les méthodes dont je avais besoin dans la maquette avec les méthodes réelles:la source
Vous pouvez vous moquer en
datetime
utilisant ceci:Dans le module
sources.py
:Dans votre
tests.py
:la source
sources
dans votre décorateur de patchs?CPython implémente en fait le module datetime en utilisant à la fois un lib / datetime.py pur-Python et un module optimisé C / _datetimemodule.c . La version optimisée C ne peut pas être corrigée, mais la version pure-Python le peut.
Au bas de l'implémentation pure-Python dans Lib / datetime.py se trouve ce code:
Ce code importe toutes les définitions optimisées C et remplace efficacement toutes les définitions pur-Python. Nous pouvons forcer CPython à utiliser l'implémentation pure-Python du module datetime en faisant:
En définissant
sys.modules["_datetime"] = None
, nous disons à Python d'ignorer le module optimisé C. Puis on recharge le module qui provoque l'importation depuis_datetime
échec de . Maintenant, les définitions pur-Python restent et peuvent être corrigées normalement.Si vous utilisez Pytest, incluez l'extrait ci-dessus dans conftest.py et vous pourrez patcher des
datetime
objets normalement.la source