Рубрики
Без рубрики

Freezegun – настоящая радость для поддельных свиданий в Python

Freezegun – это библиотека для насмешек Python Datetimes. Это делает одну вещь, делает это хорошо, и позволяет вам продолжать свою жизнь. Tagged с помощью Python, тестирование, насмешка.

Этот пост изначально появился как Гостевая статья о пибитах .

Вступление

Если вы когда -либо тестировали код, включающий даты и время в Python, вам, вероятно, пришлось издеваться над DateTime модуль. И если вы издевались над модулем DateTime, в какой -то момент он, вероятно, высмеивал вас, когда ваши тесты не удались.

Фотография Дэн Кук на Неспособный

Давайте посмотрим на некоторые проблемы, которые возникают с поддельными временами, и как Freezegun может помочь обратиться к ним.

Простой тест

Во-первых, вот фрагмент кода чувствительного к дате:

tomorrow.py

import datetime

def tomorrow():
    return datetime.date.today() + datetime.timedelta(days=1)

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

import datetime
from unittest.mock import patch, Mock

import tomorrow

fake_date = Mock(wraps=datetime.date)
fake_date.today.return_value = datetime.date(2020, 7, 2)

@patch('datetime.date', fake_date)
def test_tomorrow():
    assert tomorrow.tomorrow() == datetime.date(2020, 7, 3)

Это работает, отлично! Теперь давайте сломем это.

Каталог неудач

Допустим, во время рефакторов мы меняем:

import datetime

def tomorrow():
    return datetime.date.today() + datetime.timedelta(days=1)

к этому:

from datetime import date, timedelta

def tomorrow():
    return date.today() + timedelta(days=1)

Или, возможно, это:

from datetime import date as dt, timedelta as td

def tomorrow():
    return dt.today() + td(days=1)

Оба изменения приводят к сбою теста, даже если функциональные изменения нет. Почему?

С от даты импорта DateTime, Timedelta , тест на код, получает ссылку на непрерывное datetime.date во время импорта. К тому времени, когда тест работает, его @patch не имеет практического эффекта.

Следуя совету в “Где исправить” , мы могли бы снова получить тест, исправляя наш собственный код, а не встроенный модуль DateTime:

@patch('tomorrow.date', fake_date)
def test_tomorrow():
    assert tomorrow.tomorrow() == datetime.date(2020, 7, 3)

Это не охватит второе изменение – нам также нужно также исправить завтра для этого. Всего несколько примеров заставляют этот тест начинать чувствовать себя хрупким и тесно связанным с реализацией.

Мы можем добиться большего успеха.

Более устойчивый тест

Мы ищем тест, который может проверить поведение нашего кода, даже если изменятся тонкие детали реализации. Помимо вариаций импорта, у нас есть разные способы получить текущую дату ( date.today () , datetime.now () , datetime.utcnow () , et calea) Анкет Начиная с простейшего испытания и, работая в направлении гибкости, можно в конечном итоге получить что -то вроде этого:

fake_dttm = Mock(wraps=datetime)
fake_dttm.date.today.return_value = datetime.date(2020, 7, 2)
fake_dttm.datetime.now.return_value = datetime.datetime(2020, 7, 2)
fake_dttm.datetime.utcnow.return_value = datetime.datetime(2020, 7, 2)

@patch('tomorrow.date', fake_dttm.date, create=True)
@patch('tomorrow.datetime', fake_dttm, create=True)
@patch('tomorrow.dt', fake_dttm, create=True)
def test_tomorrow():
    assert tomorrow.tomorrow() == datetime.date(2020, 7, 3)

Это похоже на странную долину между тщательностью и прагматизмом.

Для пибита Кодекс -задача , тесты несут дополнительное внимание. Вы пишете тесты для «Официального решения», но тесты также работают против кода, полученного пользователем. Так что не должно иметь значения, использует ли один человек Импорт DateTime As DT в то время как другой выбирает от даты импорта DateTime как DT, Timedelta как td Анкет

Есть несколько разных способов справиться с этим. Я перечисляю некоторые ссылки в конце этого поста, но сейчас мы посмотрим на Freezegun библиотека.

Добавление Freezegun в наши тесты

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

fake_date = Mock(wraps=datetime.date)
fake_date.today.return_value = datetime.date(2020, 7, 2)

@patch('tomorrow.date', fake_date)
def test_tomorrow():
    assert tomorrow.tomorrow() == datetime.date(2020, 7, 3)

в это:

from freezegun import freeze_time

@freeze_time('2020-07-02')
def test_tomorrow():
    assert tomorrow.tomorrow(include_time=True) == datetime.datetime(2020, 7, 3)

Там происходит несколько аккуратных вещей. С одной стороны, мы можем использовать дружественную строку даты (любезно предоставлена превосходной The Library ), а не в ручной работы дата или DateTime объект. Это становится еще более полезным, когда мы имеем дело с полными временными метками, а не с датами.

Это также полезно (но менее сразу ясно), что freeze_time Установки ряд общих методов DateTime под капюшоном:

  • datetime.datetime.now ()
  • datetime.datetime.utcnow ()
  • datetime.date.today ()
  • time.time ()
  • Time.LocalTime ()
  • time.gmtime ()
  • Time.Strftime ()

Из -за Как Freezegun Patches эти методы, мы можем гарантировать, что замороженная дата, пока тест на коде использует эти встроенные методы. Таким образом, наши тесты будут плавно обрабатывать варианты импорта, такие как:

  • Импорт DateTime
  • с даты импорта DateTime
  • с даты импорта DateTime как DT

Глубокие и тщательные подделки

Freezegun сохраняет ваши тесты простыми, тщательно фальсифицируя Python Datetimes. Мало того, что он исправляет методы из DateTime и время , он смотрит на существующий импорт, поэтому он может быть уверен, что они тоже исправлены. Для всех, кто интересуется деталями, Этот раздел Код API Freezegun – прекрасное чтение!

Если вы бросаете свои собственные подделки, вы вряд ли будете такими же тщательными, как Freezegun. Вам, вероятно, не нужно быть! Но, по крайней мере, для некоторых случаев такая библиотека, как Freezegun, может предложить более тщательные тесты, которые также более простые и читаемые.

Принимая его дальше

Для большего объема чувствительные к производительности тесты с поддельными датами, libfaketime Может быть, стоит посмотреть. Кроме того, есть pytest плагины доступны для обоих Freezegun и LibfakeTime .

Рекомендации

Этот пост был довольно узко ориентированным взглядом на некоторые общие проблемы, которые возникают при тестировании с поддельными датами. Я не эксперт по этому поводу, но меня вдохновили люди, которые знают это лучше. Так что, если вы нашли этот пост интересным, некоторые из этих ресурсов также могут посмотреть:

А так как мы говорим о датах и времени здесь, я не могу не включить Программисты ложей верят во время .

Благодарности

Спасибо Pybites Сообщество для вдохновения этого поста. Примечательно:

  • Нитин Джордж Чериан, для начала обсуждения поддельных дат
  • Terry Spotts, для создания и рефакторирования соответствующей задачи кода
  • Боб Бельдербос, чтобы указать, что эта тема заслужила пост

И, конечно, спасибо Стив Пулет Для создания Freezegun. (Пока я в этом, спасибо за moto тоже!)

Оригинал: “https://dev.to/ajkerrigan/freezegun-real-joy-for-fake-dates-in-python-4a1i”