Как изменить дату/время в Python для всех модулей?

Когда я пишу с бизнес-логикой, мой код часто зависит от текущего времени. Например, алгоритм, который смотрит на каждый незаконченный порядок и проверяет, должен ли счет быть отправлен (который не зависит ни в какие изо дней начиная с, задание было закончено). В этих случаях, создающих счет, не инициирован явным пользовательским действием, но фоновым заданием.

Теперь это создает проблему для меня когда дело доходит до тестирования:

  • Я могу протестировать само создание счета легко
  • Однако трудно создать порядок в тесте и проверить, что фоновое задание определяет корректные заказы в корректное время.

До сих пор я нашел два решения:

  • В тестовой установке вычислите даты задания относительно текущей даты. Оборотная сторона: код становится довольно сложным, поскольку нет никаких явных дат, записанных больше. Иногда бизнес-логика довольно сложна для пограничных случаев, таким образом, становится трудно отладить из-за всех этих относительных дат.
  • У меня есть свои собственные функции средства доступа даты/времени, которые я использую всюду по своему коду. В тесте я просто назначил текущую дату, и все модули получают эту дату. Таким образом, я могу моделировать создание порядка в феврале и проверить, что счет создается в апреле легко. Оборотная сторона: сторонние модули не используют этот механизм, таким образом, это действительно твердо к integrate+test они.

Второй подход был путем, более успешным мне, в конце концов. Поэтому я ищу способ установить время datetime+time возврат модулей Python. Назначение даты обычно достаточно, я не должен устанавливать текущий час или второй (даже при том, что это было бы хорошо).

Есть ли такая утилита? Существует ли (внутренний) Python API, который я могу использовать?

20
задан Felix Schwarz 17 April 2010 в 10:22
поделиться

4 ответа

Monkey-patching time.time , вероятно, на самом деле достаточно, поскольку он обеспечивает основу почти для всех других основанных на времени подпрограмм в Python. Похоже, что это довольно хорошо справляется с вашим вариантом использования, не прибегая к более сложным трюкам, и не имеет значения, когда вы это делаете (за исключением нескольких пакетов stdlib, таких как Queue.py и threading.py, которые делают из time import time , и в этом случае вы должны пропатчить их до того, как они будут импортированы):

>>> import datetime
>>> datetime.datetime.now()
datetime.datetime(2010, 4, 17, 14, 5, 35, 642000)
>>> import time
>>> def mytime(): return 120000000.0
...
>>> time.time = mytime
>>> datetime.datetime.now()
datetime.datetime(1973, 10, 20, 17, 20)

Тем не менее, за годы имитации объектов для различных типов автоматического тестирования мне нужен был этот подход очень редко, так как в большинстве случаев он мой собственный код приложения, которому нужны насмешки, а не подпрограммы stdlib. В конце концов, вы знаете, что они уже работают. Если вы сталкиваетесь с ситуациями, когда ваш собственный код должен обрабатывать значения, возвращаемые библиотечными подпрограммами, вы можете имитировать сами библиотечные подпрограммы, по крайней мере, при проверке того, как ваше собственное приложение будет обрабатывать временные метки.

На сегодняшний день наилучшим подходом является создание вашей собственной процедуры (подпрограмм) обслуживания даты / времени, которую вы используете исключительно в коде вашего приложения, и встроенной в нее способности тестов выдавать ложные результаты по мере необходимости.Например, я иногда делаю более сложный эквивалент этого:

# in file apptime.py (for example)
import time as _time

class MyTimeService(object):
    def __init__(self, get_time=None):
        self.get_time = get_time or _time.time

    def __call__(self):
        return self.get_time()

time = MyTimeService()

Теперь в моем коде приложения я просто импортирую apptime как время; time.time () , чтобы получить текущее значение времени, тогда как в тестовом коде я могу сначала выполнить apptime.time = MyTimeService (mock_time_func) в моем коде setUp () , чтобы предоставить поддельные результаты времени.

Обновление: Спустя годы есть альтернатива, как отмечено в ответе Дэйва Форгака .

9
ответ дан 30 November 2019 в 01:08
поделиться

Вы можете исправить систему, создав собственный модуль даты и времени (даже поддельный - см. пример ниже) в качестве прокси, а затем вставьте его в словарь sys.modules. С этого момента каждый импорт в модуль datetime будет возвращать ваш прокси.
В отношении класса datetime все еще есть предостережение, особенно когда кто-то делает from datetime import datetime ; для этого вы можете просто добавить еще один прокси только для этого класса.

Вот пример того, что я говорю - конечно, это просто то, что я бросил за 5 минут, и может иметь несколько проблем (например, тип класса datetime неверен); но, надеюсь, это уже может быть полезно.

import sys
import datetime as datetime_orig

class DummyDateTimeModule(sys.__class__):
  """ Dummy class, for faking datetime module """
  def __init__(self):
    sys.modules["datetime"] = self
  def __getattr__(self, attr):
    if attr=="datetime":
      return DummyDateTimeClass()
    else:
      return getattr(datetime_orig, attr)

class DummyDateTimeClass(object):
  def __getattr__(self, attr):
    return getattr(datetime_orig.datetime, attr)

dt_fake = DummyDateTimeModule()

Наконец, стоит ли оно того?
Честно говоря, второе решение мне нравится больше, чем это :-).
Да, python - очень динамичный язык, на котором вы можете делать довольно много интересных вещей, но исправление кода таким образом всегда сопряжено с определенным риском, даже если мы говорим здесь о тестировании код.
Но в основном, я думаю, что вспомогательная функция сделает исправление тестов более явным, а также ваш код будет более ясным с точки зрения того, что он будет тестироваться, что повысит удобочитаемость.
Поэтому, если изменение не слишком дорогое, я воспользуюсь вашим вторым подходом.

2
ответ дан 30 November 2019 в 01:08
поделиться

для этого может быть несколько способов, например, создание заказов (с текущей меткой времени) и последующее изменение этого значения в БД напрямую каким-либо внешним процессом (при условии, что данные находятся в БД).

Я предлагаю кое-что еще. Вы когда-нибудь задумывались о том, чтобы запустить приложение на виртуальной машине, установить время, чтобы сказать «февраль», создать заказы, а затем просто изменить время виртуальных машин? Этот подход максимально приближен к реальной жизненной ситуации.

0
ответ дан 30 November 2019 в 01:08
поделиться

Один из способов сделать это - динамически исправить модуль времени / даты и времени

, например,

import time
import datetime

class MyDatetime:

    def now(self):
        return time.time()

datetime.datetime = MyDatetime

print datetime.datetime().now()
1
ответ дан 30 November 2019 в 01:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: