【发布时间】:2013-12-28 11:53:30
【问题描述】:
我需要测试使用datetime.datetime.now() 的函数。最简单的方法是什么?
【问题讨论】:
标签: python datetime mocking pytest
我需要测试使用datetime.datetime.now() 的函数。最简单的方法是什么?
【问题讨论】:
标签: python datetime mocking pytest
您需要对 datetime.now 函数进行monkeypatch。在下面的示例中,我正在创建可以稍后在其他测试中重复使用的夹具:
import datetime
import pytest
FAKE_TIME = datetime.datetime(2020, 12, 25, 17, 5, 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
【讨论】:
now方法?
utcnow
mydatetime 类应该是datetime.datetime 的子类,否则它将无法执行 datetime.datetime 正常执行的任何其他方法。
from datetime import datetime
from freezegun import freeze_time # $ pip install freezegun
@freeze_time("Jan 14th, 2012")
def test_nice_datetime():
assert datetime.now() == datetime(2012, 1, 14)
freeze_time() 也可以用作上下文管理器。模块支持指定本地时区 UTC 偏移量。
【讨论】:
这是我用于覆盖 now() 但保持其余日期时间工作的夹具(RE:satoru 的问题)。
它没有经过广泛的测试,但它确实解决了在其他上下文中使用日期时间的问题。对我来说,让 Django ORM 使用这些日期时间值(特别是 isinstance(Freeze.now(), datetime.datetime) == True)非常重要。
@pytest.fixture
def freeze(monkeypatch):
""" Now() manager patches datetime return a fixed, settable, value
(freezes time)
"""
import datetime
original = datetime.datetime
class FreezeMeta(type):
def __instancecheck__(self, instance):
if type(instance) == original or type(instance) == Freeze:
return True
class Freeze(datetime.datetime):
__metaclass__ = FreezeMeta
@classmethod
def freeze(cls, val):
cls.frozen = val
@classmethod
def now(cls):
return cls.frozen
@classmethod
def delta(cls, timedelta=None, **kwargs):
""" Moves time fwd/bwd by the delta"""
from datetime import timedelta as td
if not timedelta:
timedelta = td(**kwargs)
cls.frozen += timedelta
monkeypatch.setattr(datetime, 'datetime', Freeze)
Freeze.freeze(original.now())
return Freeze
也许题外话了,但可能对其他提出这个问题的人有用。这个夹具允许“冻结”时间,然后在您的测试中随意来回移动它:
def test_timesensitive(freeze):
freeze.freeze(2015, 1, 1)
foo.prepare() # Uses datetime.now() to prepare its state
freeze.delta(days=2)
# Does something that takes in consideration that 2 days have passed
# i.e. datetime.now() returns a date 2 days in the future
foo.do_something()
assert foo.result == expected_result_after_2_days
【讨论】:
使用MagicMock(wrap=datetime.datetime) 怎么样?
此方法模拟datetime.datetime.now(),但其他方法与原始datetime.datetime 相同。
from unittest.mock import MagicMock
def test_datetime_now(monkeypatch):
import datetime
FAKE_NOW = datetime.datetime(2020, 3, 11, 14, 0, 0)
datetime_mock = MagicMock(wraps=datetime.datetime)
datetime_mock.now.return_value = FAKE_NOW
monkeypatch.setattr(datetime, "datetime", datetime_mock)
assert datetime.datetime.now() == FAKE_NOW
# the other methods are available
assert datetime.datetime.fromisoformat("2020-03-01T00:00:00") == datetime.datetime(2020, 3, 1, 0, 0, 0)
这里是使用@pytest.fixture的方法。
import datetime
from unittest.mock import MagicMock
import pytest
FAKE_NOW = datetime.datetime(2020, 3, 11, 14, 0, 0)
@pytest.fixture()
def mock_datetime_now(monkeypatch):
datetime_mock = MagicMock(wraps=datetime.datetime)
datetime_mock.now.return_value = FAKE_NOW
monkeypatch.setattr(datetime, "datetime", datetime_mock)
def test_datetime_now2(mock_datetime_now):
assert datetime.datetime.now() == FAKE_NOW
assert datetime.datetime.fromisoformat("2020-03-01T00:00:00") == datetime.datetime(2020, 3, 1, 0, 0, 0)
【讨论】:
MagicMock。所以按原样编写代码是行不通的。
isinstance(value, datetime.datetime)的代码,例如,tortoise-orm。
改编自其他答案:
import datetime as dt
from contextlib import contextmanager
from unittest.mock import patch
@contextmanager
def mocked_now(now):
class MockedDatetime(dt.datetime):
@classmethod
def now(cls):
return now
with patch("datetime.datetime", MockedDatetime):
yield
像这样使用:
def test_now():
with mocked_now(dt.datetime(2017, 10, 21)):
assert dt.datetime.now() == dt.datetime(2017, 10, 21)
【讨论】:
patch 来自哪里?您错过了导入吗?
from contextlib import contextmanager