214 votes

Python: Essayer de se moquer de datetime.date.today () mais ne fonctionne pas

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 un meilleur moyen?

199voto

Mehdi Behrooz Points 351

Une autre option consiste à utiliser https://github.com/spulec/freezegun/

Installez-le:

 pip install freezegun
 

Et utilisez-le:

 from freezegun import freeze_time

@freeze_time("2012-01-01")
def test_something():

    from datetime import datetime
    print(datetime.now()) #  2012-01-01 00:00:00

    from datetime import date
    print(date.today()) #  2012-01-01
 

Cela affecte également les autres appels datetime dans les appels de méthode d'autres modules:

autre_module.py:

 from datetime import datetime

def other_method():
    print(datetime.now())    
 

main.py:

 from freezegun import freeze_time

@freeze_time("2012-01-01")
def test_something():

    import other_module
    other_module.other_method()
 

Et enfin:

 $ python main.py
# 2012-01-01
 

151voto

user3016183 Points 11

Pour ce qui en vaut la peine, les documents Mock parlent spécifiquement de datetime.date.today, et il est possible de le faire sans avoir à créer une classe fictive:

http://mock.readthedocs.org/en/latest/examples.html#further-examples

 >>> from datetime import date
>>> with patch('mymodule.date') as mock_date:
...     mock_date.today.return_value = date(2010, 10, 8)
...     mock_date.side_effect = lambda *args, **kw: date(*args, **kw)
...
...     assert mymodule.date.today() == date(2010, 10, 8)
...     assert mymodule.date(2009, 6, 8) == date(2009, 6, 8)
...
 

146voto

Daniel G Points 12647

Il y a quelques problèmes.

Tout d'abord, la façon dont vous utilisez mock.patch n'est pas tout à fait droit. Lorsqu'il est utilisé comme décorateur, il remplace la fonction donnée/classe (dans ce cas, datetime.date.today) avec un Mock objet que dans le décoré de la fonction. Alors, seulement à l'intérieur de votre today() va datetime.date.today être une fonction différente, ce qui ne semble pas être ce que vous voulez.

Ce que vous voulez vraiment semble être plus comme ceci:

@mock.patch('datetime.date.today')
def test():
    datetime.date.today.return_value = date(2010, 1, 1)
    print datetime.date.today()

Malheureusement, cela ne fonctionne pas:

>>> test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "build/bdist.macosx-10.6-universal/egg/mock.py", line 557, in patched
  File "build/bdist.macosx-10.6-universal/egg/mock.py", line 620, in __enter__
TypeError: can't set attributes of built-in/extension type 'datetime.date'

Cela échoue, car Python types intégrés sont immuables - voir cette réponse pour plus de détails.

Dans ce cas, je sous-classe datetime.date moi-même et de créer le droit de la fonction:

import datetime
class NewDate(datetime.date):
    @classmethod
    def today(cls):
        return cls(2010, 1, 1)
datetime.date = NewDate

Et maintenant, vous pouvez faire:

>>> datetime.date.today()
NewDate(2010, 1, 1)

41voto

israelord Points 776

Je suppose que je suis venu un peu tard pour cela, mais je pense que le principal problème ici est que vous êtes correctifs de type datetime.date.aujourd'hui directement et, d'après la documentation, ce qui est faux.

Vous devriez patch la référence importés dans le fichier où l'testé la fonction est, par exemple.

Disons que vous avez un functions.py fichier où vous avez la suivante:

import datetime

def get_today():
    return datetime.date.today()

puis, dans votre test, vous devriez avoir quelque chose comme ceci

import unittest
from functions import get_today
from mock import patch, Mock

class GetTodayTest(unittest.TestCase):

    @patch('functions.datetime')
    def test_get_today(self, datetime_mock):
        datetime_mock.date.today = Mock(return_value=datetime.strptime('Jun 1 2005', '%b %d %Y'))
        value = get_today()
        # then assert your thing...

Espérons que cela aide un peu.

35voto

eternicode Points 2892

Pour ajouter à la solution de Daniel G:

 from datetime import date

class FakeDate(date):
    "A manipulable date replacement"
    def __new__(cls, *args, **kwargs):
        return date.__new__(date, *args, **kwargs)
 

Cela crée une classe qui, lorsqu'elle est instanciée, retournera un objet datetime.date normal, mais qui peut également être modifiée.

 @mock.patch('datetime.date', FakeDate)
def test():
    from datetime import date
    FakeDate.today = classmethod(lambda cls: date(2010, 1, 1))
    return date.today()

test() # datetime.date(2010, 1, 1)
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X