web-dev-qa-db-fra.com

Python: Comment me moquer de datetime.utcnow ()?

J'ai le ci-dessous:

from datetime import datetime

def get_report_month_key():
    month_for_report = datetime.utcnow()
    return month_for_report.strftime("%Y%m") 

Comment me moquer de datetime.utcnow () pour pouvoir écrire un test unitaire sur cette fonction?

J'ai essayé de lire ceci one mais je suis incapable de le faire fonctionner pour moi sur utcnow ()

10
Steven Yong

dans votre fichier de test:

from yourfile import get_report_month_key
import mock
import unittest
from datetime import datetime

class TestCase(unittest.TestCase):

    @mock.patch('yourfile.datetime')
    def test_dt(self, mock_dt):
        mock_dt.utcnow = mock.Mock(return_value=datetime(1901, 12, 21))
        r = get_report_month_key()
        self.assertEqual('190112', r)
20
dasjotre

La réponse acceptée par dasjotre fonctionne si vous ne créez aucune instance datetime dans le module que vous testez. Si vous essayez de créer un datetime, il créera un objet fictif au lieu de celui avec les méthodes attendues sur un objet standard datetime. En effet, cela remplace toute la définition de classe par un faux. Au lieu de cela, vous pouvez utiliser une approche similaire pour créer la définition simulée en utilisant datetime comme base.

mymodule.py

from datetime import datetime

def after_y2k():
    y2k = datetime(2000, 1, 1)
    return y2k < datetime.utcnow()

test_mymodule.py

import unittest
import datetime
from mock import patch, Mock
import mymodule
from mymodule import after_y2k


class ModuleTests(unittest.TestCase):
    @patch.object(mymodule, 'datetime', Mock(wraps=datetime.datetime))
    def test_after_y2k_passes(self):
        # Mock the return and run your test (Note you are doing it on your module)
        mymodule.datetime.utcnow.return_value = datetime.datetime(2002, 01, 01)
        self.assertEqual(True, after_y2k())

        mymodule.datetime.utcnow.return_value = datetime.datetime(1999, 01, 01)
        self.assertEqual(False, after_y2k())

    @patch('mymodule.datetime')
    def test_after_y2k_fails(self, mock_dt):
        # Run your tests
        mock_dt.utcnow = Mock(return_value=datetime.datetime(2002, 01, 01))
        self.assertEqual(True, after_y2k())

        # FAILS!!! because the object returned by utcnow is a MagicMock w/o 
        # datetime methods like "__lt__"
        mock_dt.utcnow = Mock(return_value=datetime.datetime(1999, 01, 01))
        self.assertEqual(False, after_y2k())
4
Ryan Widmaier

Ce qui fonctionne également lors de la correction des modules Python intégrés s'avère être compliqué (comme avec datetime, voir par exemple https://solidgeargroup.com/mocking-the- time ou https: // nedbatchelder. com/blog/201209/mocking_datetimetoday.html ou https://Gist.github.com/rbarrois/5430921 ) encapsule la fonction dans une fonction personnalisée pouvant ensuite être facilement corrigée.

Donc, au lieu d’appeler datetime.datetime.utcnow(), vous utilisez une fonction comme

import datetime


def get_utc_now():
    return datetime.datetime.utcnow()

Ensuite, patcher celui-ci est aussi simple que

import datetime

# use whatever datetime you need here    
fixed_now = datetime.datetime(2017, 8, 21, 13, 42, 20)
with patch('your_module_name.get_utc_now', return_value=fixed_now):
    # call the code using get_utc_now() here
    pass

L'utilisation du décorateur patch à la place du gestionnaire de contexte fonctionnerait de la même manière.

3
Dirk

Vous pouvez essayer d'utiliser le module freezetime.

from yourfile import get_report_month_key
from freezegun import freeze_time
import unittest

class TestCase(unittest.TestCase):

    @freeze_time('2017-05-01')
    def get_report_month_key_test():
       get_report_month_key().should.equal('201705')
0
leannez