I have the below:
from datetime import datetime
def get_report_month_key():
month_for_report = datetime.utcnow()
return month_for_report.strftime(\"
What also works when patching built-in Python modules turns out to be complicated (as it is with datetime
, see e.g. https://solidgeargroup.com/mocking-the-time or https://nedbatchelder.com/blog/201209/mocking_datetimetoday.html or https://gist.github.com/rbarrois/5430921) is wrapping the function in a custom one which then can be easily patched.
So, instead of calling datetime.datetime.utcnow()
, you use a function like
import datetime
def get_utc_now():
return datetime.datetime.utcnow()
Then, patching this one is as simple as
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
Using the patch
decorator instead of the context manager would work similarly.
The accepted answer by dasjotre works if you don't create any datetime
instances in the module you are testing. If you try to create a datetime
it will create a Mock object instead of one with the expected methods on a standard datetime
object. This is because it replaces the whole class definition with a mock. Instead of doing this, you can use a similar approach to create the mocked definition by using datetime
as the 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())
in your test file:
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)
You can try using freezetime module.
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')
If your code is in another file you need to patch where the import happens (lets call your file file1.py):
from file1 import get_report_month_key
import mock
@mock.patch("get_report_month_key.datetime.utcnow")
def test_get_report_month_key(mock_utcnow):
mock_utcnow.return_value = "your value"
assert get_report_month_key() == "your expected value"
Of course, I would wrap it with unittest framework.