如何用py.test monkeypatch python的datetime.datetime.now?

sas*_*shk 23 python datetime mocking pytest

我需要测试使用的功能datetime.datetime.now().最简单的方法是什么?

sas*_*shk 31

你需要monkeypatch datetime.now函数.在下面的例子中,我正在创建夹具,我可以在以后的其他测试中重复使用:

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
Run Code Online (Sandbox Code Playgroud)

  • 这有效,但如果您执行“从日期时间导入日期时间”则无效。在这里查看详细信息:http://stackoverflow.com/questions/35431476/why-pythons-monkeypatch-doesnt-work-when-importing-a-class-instead-of-a-module (5认同)
  • 您的“mydatetime”类应该是“datetime.datetime”的子类,否则它将无法执行 datetime.datetime 通常执行的任何其他方法。 (5认同)
  • 是否可以仅替换“ now”方法? (3认同)

jfs*_*jfs 21

freezegun模块:

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)
Run Code Online (Sandbox Code Playgroud)

freeze_time()也可以用作上下文管理器.该模块支持指定本地时区UTC偏移量.


Seb*_*ian 7

这是我用于覆盖 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
Run Code Online (Sandbox Code Playgroud)

也许偏离主题,但可能对其他提出这个问题的人有用。这个装置允许“冻结”时间,然后在你的测试中随意来回移动它:

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
Run Code Online (Sandbox Code Playgroud)


小智 6

如果您是 pytest-mock 的爱好者,请datetime.now使用它来模拟。

from datetime import datetime, timezone

def test_time_now(mocker):
    #patch the the datetime method in your file

    mock_date = mocker.patch("your_package.your_package.datetime")
    FAKE_NOW = datetime(2020, 3, 11, 14, 0, 0, tzinfo=timezone.utc)
    mock_date.now.return_value = FAKE_NOW
    
    from your_package.your_package import time_to_test
    timestamp_method = time_to_test()
    assert timestamp_method == FAKE_NOW
Run Code Online (Sandbox Code Playgroud)


Shi*_*oto 5

怎么用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)

Run Code Online (Sandbox Code Playgroud)

使用@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)

Run Code Online (Sandbox Code Playgroud)