如何在pytest中模拟/设置系统日期?

32 投票
4 回答
32363 浏览
提问于 2025-04-18 08:15

在我的一些测试中,我遇到了一个问题,就是它们在Travis上因为时间和时区的问题而失败。所以我想在我的测试中模拟系统时间。我该怎么做呢?

4 个回答

4

我更喜欢用这段代码。

from unittest.mock import MagicMock


@pytest.fixture
def mocking_datetime_now(monkeypatch):
    datetime_mock = MagicMock(wrap=datetime.datetime)
    datetime_mock.now.return_value = datetime.datetime(2020, 3, 11, 0, 0, 0)

    monkeypatch.setattr(datetime, "datetime", datetime_mock)


@pytest.fixture
def setup_db(company, company_user, mocking_datetime_now):
    assert datetime.datetime.now() == datetime.datetime(2020, 3, 11, 0, 0, 0)
18

你可以通过两种方式来实现这个目标:

  1. 创建一个函数,代替 datetime.datetime.now() 的调用,正如Bruno所建议的,不过这里有不同的实现方式:

    import os
    import datetime
    
    def mytoday():
     if 'MYDATE' in os.environ:
         return datetime.datetime.strptime(os.getenv('MYDATE'), '%m-%d-%Y').date()
     else:
         return datetime.date.today()
    

    然后,在你的测试中,你只需要修改环境变量:

    import datetime
    
    def test_patched_date(monkeypatch):
        monkeytest.setenv('MYDATE', '05-31-2014')
        assert datetime.date.today() == datetime.date(2014, 5, 31)
    
  2. 直接修改 datetime 函数:

    import datetime
    import pytest
    
    FAKE_TIME = datetime.datetime(2020, 12, 25, 17, 05, 55)
    
    @pytest.fixture
    def patch_datetime_now(monkeypatch):
    
        class mydatetime:
            @classmethod
            def now(cls):
                return FAKE_TIME
    
        monkeypatch.setattr(datetime, 'datetime', mydatetime)
    
    
    def test_patch_datetime(patch_datetime_now):
        assert datetime.datetime.now() == FAKE_TIME
    
44

@Brian-Kruger's 的回答是最好的。我已经投票让它恢复可见。与此同时……

可以使用 freezegun代码库)。

来自说明文档:

from freezegun import freeze_time

@freeze_time("2012-01-14")
def test():
    assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)
29

据我所知,你不能模拟内置的方法。

我常用的一种方法是稍微修改一下我的代码,不直接使用 datetime 来获取日期,而是使用一个包装函数:

# mymodule.py

def get_today():
   return datetime.date.today()

这样在测试的时候就很简单地可以 mock 它了:

def test_something():
    with mock.patch('mymodule.get_today', return_value=datetime.date(2014, 6, 2)):
        ...

你也可以使用 freezegun 这个模块。

撰写回答