我很困惑为什么用冻结枪冻结时间的函数根据是否datetime.datetime.utcnow()被调用输出不同的UTC时间,或者datetime.datetime.now(pytz.utc).我不是说它坏了,只是因为我不明白为什么,而且想知道!
例如,使用此功能:
@freeze_time("2012-01-14 03:21:34", tz_offset=-4)
def test():
print("utcnow(): %s" % datetime.datetime.utcnow())
print("pytz.utc: %s" % datetime.datetime.now(pytz.utc))
Run Code Online (Sandbox Code Playgroud)
输出是:
utcnow(): 2012-01-14 03:21:34
pytz.utc: 2012-01-13 23:21:34+00:00
Run Code Online (Sandbox Code Playgroud)
我想第一个是天真的约会时间,但为什么它们的时间不同?
(最终为什么我想知道:如果我在我的测试中使用freezegun,并且我在我的代码中使用pytz生成时间进行测试,我想知道它的"正确"行为应该是什么.)
我在这里写一个功能测试来检查我的API限制是否按预期工作(将在每个月的开始时休息).
测试类:
class ApiThrottlingTest(ThrottlingBaseTest):
def test_throttling_purchaser_case(self):
now = datetime.datetime(year=2015, month=1, day=10, hour=6, minute=6, second=3)
last_day_of_current_month = datetime.datetime(year=2015, month=1, day=31, hour=23, minute=59, second=59)
first_day_of_next_month = datetime.datetime(year=2015, month=2, day=1, hour=0, minute=0, second=0)
with freeze_time(now) as frozen_datetime:
for i in xrange(3):
resp = self._project_details_request()
self.assertEqual(resp.status_code, 200)
resp = self._project_details_request()
self.assertEqual(resp.status_code, 429)
frozen_datetime.move_to(last_day_of_current_month)
resp = self._project_details_request()
# the test fails at this level
self.assertEqual(resp.status_code, 429)
frozen_datetime.move_to(first_day_of_next_month)
resp = self._project_details_request()
self.assertEqual(resp.status_code, 200)
Run Code Online (Sandbox Code Playgroud)
如果出现以下情况,测试工作正常:last_day_of_current_month = datetime.datetime(... second=0)
但如果出现以last_day_of_current_month = datetime.datetime(... second=59)
在调试之后,似乎timeDjangoRestFramework …
我至少从 2015 年就注意到了这种行为,并且从那以后就没有改变。当使用 freezegun (或 pytest-freezegun)冻结测试中的时间时,datetime.datetime.now()返回冻结值 whilepd.Timestamp('now')和pd.to_datetime('now')do not。有没有解决的办法?
例如:https: //pypi.org/project/pytest-freezegun/
简单模型(models.py):
from django.db import models
class MyModel(models.Model):
start_date = models.DateField()
Run Code Online (Sandbox Code Playgroud)
简单工厂(test_factories.py):
from datetime import date
import factory
from .models import MyModel
class MyModelFactory(factory.django.DjangoModelFactory):
class Meta:
model = MyModel
start_date = date.today()
Run Code Online (Sandbox Code Playgroud)
在manage.py shell:
In [1]: from datetime import date
In [2]: from freezegun import freeze_time
In [3]: from polls.test_factories import MyModelFactory
In [4]: date.today()
Out[4]: datetime.date(2017, 8, 16)
In [5]: with freeze_time(date(1999,9,9)):
...: print(date.today())
...: m = MyModelFactory()
...: print(m.start_date)
...:
1999-09-09
2017-08-16
Run Code Online (Sandbox Code Playgroud)
当前日期是 2017-08-16,假日期是 1999-09-09。在里面freeze_time, …
我有一个参数默认值为 的函数datetime.now()。方法如下,
def as_standard_format(p_date=datetime.now(), fmt=sdk_constants.DEFAULT_DATE_TIME_FORMAT):
return p_date.strftime(fmt)
Run Code Online (Sandbox Code Playgroud)
我有一个类似下面的测试方法,
@freeze_time(datetime(year=2018, month=9, day=25, hour=15, minute=46, second=36))
def testNowWithDefaultFormat(self):
# The below method gives the frozen time
print(datetime.now())
# But the below test fails
assert utils.format_date(
datetime.now()) == "20180925T154636Z", "Default call not giving current time in " \
"standard format \"%Y%m%dT%H%M%SZ\""
Run Code Online (Sandbox Code Playgroud)
为什么不能freeze_time使用默认参数值?
我使用以下模型创建了一个 SQLAlchemy 应用程序:
class MyObject(db.Model):
__tablename__ = 'my_object'
id = db.Column(db.Integer, nullable=False, primary_key=True, autoincrement=True)
some_string = db.Column(db.String(20), nullable=False)
created = db.Column(db.DateTime, default=datetime.datetime.now)
updated = db.Column(db.DateTime, default=datetime.datetime.now, onupdate=datetime.datetime.now)
Run Code Online (Sandbox Code Playgroud)
我创建了这个迁移文件来配合它:
"""
Junk
Revision ID: 4b6fffffffff_
Revises: 4b6e7775856f
Create Date: 2019-11-08 00:31:13.297355
"""
# revision identifiers, used by Alembic.
revision = '4b6fffffffff_'
down_revision = '4b6e7775856f'
from alembic import op
import sqlalchemy as sa
import sqlalchemy_utils
from sqlalchemy import false
def upgrade():
op.create_table(
'my_object',
sa.Column('created', sa.DateTime(), nullable=False),
sa.Column('updated', sa.DateTime(), nullable=False),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('some_string', …Run Code Online (Sandbox Code Playgroud) 我试图在我的单元测试中使用 freezegun 来修补数据类中的字段,该字段设置为对象初始化时的当前日期。我想这个问题与任何修补被用作 default_factory 的函数的尝试有关,只是在 freezegun 之外。数据类被冻结,所以它是不可变的。
例如,如果我的数据类是:
@dataclass(frozen=True)
class MyClass:
name: str
timestamp: datetime.datetime = field(init=False, default_factory=datetime.datetime.now)
Run Code Online (Sandbox Code Playgroud)
当我使用 freezegun 修补 datetime 时,它对 MyClass 中时间戳的初始化没有影响(它仍然将时间戳设置为单元测试中 now() 返回的当前日期,导致测试失败)。
我假设它与在补丁到位之前加载的默认工厂和模块有关。我尝试修补日期时间,然后使用 importlib.reload 重新加载模块,但没有运气。
我目前的解决方案是:
@dataclass(frozen=True)
class MyClass:
name: str
timestamp: datetime.datetime = field(init=False)
def __post_init__(self):
object.__setattr__(self, "timestamp", datetime.datetime.now())
Run Code Online (Sandbox Code Playgroud)
哪个有效。
理想情况下,我想要一个非侵入性的解决方案,它不需要我更改我的生产代码来启用我的单元测试。
freezegun ×7
python ×6
django ×2
unit-testing ×2
datetime ×1
factory-boy ×1
pandas ×1
patch ×1
pytz ×1
sqlalchemy ×1