How to mock python's datetime.now() in a class method for unit testing?

前端 未结 7 1229
南旧
南旧 2020-12-14 05:38

I\'m trying to write tests for a class that has methods like:

import datetime
import pytz

class MyClass:
    def get_now(self, timezone):
        return dat         


        
相关标签:
7条回答
  • 2020-12-14 06:12

    If you don't want to install anything this is the simplest way. Simply use, Mock class -

    class NewDt(datetime.date):
        @classmethod
         def now(cls):
               return datetime.datetime.strptime('2020-07-10 05:20:20', '%Y-%m-%d %H:%M:%S')
    

    And use this patch before mock function

     @mock.patch('module path', NewDt)
    
    0 讨论(0)
  • I'm using date, but the same idea should work for datetime:

    class SpoofDate(date):
        def __new__(cls, *args, **kwargs):
            return date.__new__(date, *args, **kwargs)
    

    ...

    from mock import patch
    
    @patch('some.module.date', SpoofDate)
    def testSomething(self):
        SpoofDate.today = classmethod(lambda cls : date(2012, 9, 24))
    

    Where some.module imports date. Patch is replacing the imported date with SpoofDate, which you can then redefine to do whatever you want.

    0 讨论(0)
  • 2020-12-14 06:13

    Having asked this question originally...

    As @Jocelyn delalande suggested, I've been happily using freezegun for years now.

    Another option is python-libfaketime, which can be much faster than freezegun, but doesn't work on Windows and sounds a bit fiddly.

    A newer option is time-machine, introduced in this blog post that compares the three options.

    0 讨论(0)
  • 2020-12-14 06:17

    You'd create a function that returns a specific datetime, localized to the timezone passed in:

    import mock
    
    def mocked_get_now(timezone):
        dt = datetime.datetime(2012, 1, 1, 10, 10, 10)
        return timezone.localize(dt)
    
    @mock.patch('path.to.your.models.MyClass.get_now', side_effect=mocked_get_now)
    def your_test(self, mock_obj):
        # Within this test, `MyClass.get_now()` is a mock that'll return a predictable
        # timezone-aware datetime object, set to 2012-01-01 10:10:10.
    

    That way you can test if the resulting timezone-aware datetime is correctly being handled; results elsewhere should show the correct timezone but will have a predictable date and time.

    You use the mocked_get_now function as a side-effect when mocking get_now; whenever code calls get_now the call is recorded by mock, and mocked_get_now is called, and it's return value used as the value returned to the caller of get_now.

    0 讨论(0)
  • 2020-12-14 06:19

    Using patch of unittest.mock

    from unittest.mock import patch
    
    @patch('MyClass.datetime')
    def test_foo(self, mock_datetime):
        mock_datetime.datetime.now.return_value = datetime.datetime(2019, 5, 7) #SOME_MOCKED_DATE
    

    Note that we're overriding datetime module that is imported only in our class

    The class for which we are writing the test:

    import datetime
    
    class MyClass:
        def foo():
           localtime_now = datetime.datetime.now(timezone)
    

    We need not have to separate it as get_now() method just to make it easier to mock.

    0 讨论(0)
  • 2020-12-14 06:28

    You could use freezegun :

    from freezegun import freeze_time
    
    def test():
        assert datetime.datetime.now() != datetime.datetime(2012, 1, 14)
        with freeze_time("2012-01-14"):
            assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)
        assert datetime.datetime.now() != datetime.datetime(2012, 1, 14)
    

    It basically mocks datetime module calls.

    0 讨论(0)
提交回复
热议问题