Python: How do I mock datetime.utcnow()?

Question:

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()

Asked By: Steven Yong

||

Answers:

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.

Answered By: Baryo

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)
Answered By: dasjotre

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.

Answered By: Dirk

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())
Answered By: Ryan Widmaier

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')
Answered By: leannez

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.

Answered By: Abhishek Kumar
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.