webentwicklung-frage-antwort-db.com.de

Python: Wie verspotte ich datetime.utcnow ()?

Ich habe das Folgende:

from datetime import datetime

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

Wie versuche ich, datetime.utcnow () zu verspotten, damit ich einen Unit-Test für diese Funktion schreiben kann?

Ich habe versucht, dieses eine zu lesen, kann es aber bei utcnow () nicht zum Laufen bringen.

10
Steven Yong

in Ihrer Testdatei:

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

Die akzeptierte Antwort von dasjotre funktioniert, wenn Sie in dem von Ihnen getesteten Modul keine datetime-Instanzen erstellen. Wenn Sie versuchen, eine datetime zu erstellen, wird ein Mock-Objekt anstelle eines Objekts mit den erwarteten Methoden für ein Standardobjekt datetime erstellt. Dies liegt daran, dass die gesamte Klassendefinition durch einen Schein ersetzt wird. Anstatt dies zu tun, können Sie einen ähnlichen Ansatz verwenden, um die gespielte Definition mithilfe von datetime als Basis zu erstellen.

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

Was auch beim Patchen von integrierten Python-Modulen funktioniert, erweist sich als kompliziert (wie bei datetime, siehe zB https://solidgeargroup.com/mocking-the-time oder https: // nedbatchelder com/blog/201209/mocking_datetimetoday.html oder https://Gist.github.com/rbarrois/5430921 ) umgibt die Funktion in einer benutzerdefinierten Funktion, die dann problemlos gepatcht werden kann.

Anstatt datetime.datetime.utcnow() aufzurufen, verwenden Sie eine Funktion wie

import datetime


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

Dann ist das Patchen dieses so einfach wie

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

Die Verwendung des Dekorators patch anstelle des Kontextmanagers funktioniert ähnlich.

3
Dirk

Sie können das freezetime-Modul verwenden.

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