Reputation: 5426
I have the below:
from datetime import datetime
def get_report_month_key():
month_for_report = datetime.utcnow()
return month_for_report.strftime("%Y%m")
How do I mock datetime.utcnow() so that I can write unit test on this function?
Tried reading this one but I am unable to get it working for me on utcnow()
Upvotes: 42
Views: 36917
Reputation: 307
The excepted answer works fine for most cases, I encountered one case where it doesn't work, when datatime is not called from a function.
Example:
time_0 = datetime.utcnow()
class Service():
time_1 = datetime.utcnow()
def __init__(self):
time_2 = datetime.utcnow()
Here time_0, time_1 will not be mocked. To fix this use time_2 like initialization.
Upvotes: 0
Reputation: 255
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')
Upvotes: 10
Reputation: 8513
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())
Upvotes: 38
Reputation: 10111
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.
Upvotes: 16
Reputation: 641
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)
Upvotes: 59
Reputation: 324
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.
Upvotes: -2