Подтвердить что ты не робот

Как издеваться над python datetime.now() в методе класса для модульного тестирования?

Я пытаюсь написать тесты для класса с такими методами, как:

import datetime
import pytz

class MyClass:
    def get_now(self, timezone):
        return datetime.datetime.now(timezone)

    def do_many_things(self, tz_string='Europe/London'):
        tz = pytz.timezone(tz_string)
        localtime_now = self.get_now(tz)
        ...
        return things

Я хочу протестировать его, и для этого мне нужно убедиться, что вызов datetime.datetime.now() возвращает что-то предсказуемое.

Я читал много примеров использования Mock в тестах, но не нашел ничего похожего на то, что мне нужно, и я не могу понять, как использовать его в моих тестах.

Я разделил метод get_now() на случай, если было бы проще высмеять это, вместо datetime.datetime.now(), но я все еще тупик. Любые мысли о том, как писать UnitTests для этого с помощью Mock? (Это все в Django, fwiw, я не уверен, что это имеет значение в этом случае.)

4b9b3361

Ответ 1

Вы создали бы функцию, которая возвращает определенное datetime, локализованное в часовом поясе, переданном в:

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.

Таким образом, вы можете проверить, правильно ли обрабатывается результирующая дата-время, соответствующая часовому поясу; результаты в другом месте должны показывать правильный часовой пояс, но будут иметь предсказуемую дату и время.

Вы используете функцию mocked_get_now как побочный эффект, когда насмехаетесь get_now; когда код вызывает get_now, вызов записывается с помощью mock и вызывается mocked_get_now, а возвращаемое значение используется как значение, возвращаемое вызывающей стороне get_now.

Ответ 2

Вы можете использовать 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)

Это в основном высмеивает вызовы модуля datetime.

Ответ 3

Я использую date, но эта же идея должна работать для 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))

Где some.module импортирует date. Патч заменяет импортированный date на SpoofDate, который вы можете переопределить, чтобы делать все, что хотите.

Ответ 4

Я использовал бы помощников из пакета testfixtures, чтобы издеваться над классом datetime, который вы вызываете сейчас():

http://packages.python.org/testfixtures/datetime.html#datetimes

Таким образом, вы можете проверить все случаи, которые у вас есть, все время.

Ответ 5

Использование патча 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

Обратите внимание, что мы переопределяем модуль datetime, который импортируется только в наш класс

Класс, для которого мы пишем тест:

import datetime

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

Нам не нужно разделять его как метод get_now(), чтобы было проще его высмеивать.