如何冻结 datetime.now 进行单元测试
How to freeze datetime.now for unit testing
我有一个模型,它使用函数来 return 默认日期时间:
class Company(models.Model):
q1_results_date = models.DateField(
verbose_name='Q1 financial results',
default=quarter_results_date(1),
blank=False,
null=False,
)
def quarter_results_date(month):
return datetime.datetime(
datetime.datetime.now().year,
month,
calendar.monthrange(datetime.datetime.now().year, month)[1]
)
我想对此进行单元测试,这需要我将 datetime.now() 设置为已知值。为此,我使用 freezegun.freeze_time
:
def test_quarter_results_date(self):
with freeze_time("2012-01-14"):
print('check datetime.now()', datetime.now())
c = Company.objects.create()
...
然而,尽管 print
语句显示 2012-01-14
,日期时间并未冻结,因为它在评估 c1.q1_results_date
时仍然使用今天的日期。
我该如何纠正?
这不起作用的原因是因为您调用了函数。因此,这意味着 datetime
在 class 被解释时被评估,所以这基本上是在您启动服务器时。在那一刻冻结枪还没有激活。
这也意味着,如果您稍后 运行 服务器运行了很长时间,并且年份增加了,它仍将使用旧值。
您可以将可调用对象传递给默认值,从而使用辅助函数,例如:
def quarter_results_date(month):
yr = datetime.datetime.now().year
__, dy = calendar.monthrange(yr, month)
return datetime.datetime(
yr,
month,
dy
)
def <b>quarter_results_date_first</b>():
return quarter_results_date(1)
class Company(models.Model):
q1_results_date = models.DateField(
verbose_name='Q1 financial results',
default=<b>quarter_results_date_first</b>,
blank=False,
null=False,
)
请注意,default=quarter_results_date_first
没有使用括号,因此我们将引用传递给函数,而不是日期时间值。
我有一个模型,它使用函数来 return 默认日期时间:
class Company(models.Model):
q1_results_date = models.DateField(
verbose_name='Q1 financial results',
default=quarter_results_date(1),
blank=False,
null=False,
)
def quarter_results_date(month):
return datetime.datetime(
datetime.datetime.now().year,
month,
calendar.monthrange(datetime.datetime.now().year, month)[1]
)
我想对此进行单元测试,这需要我将 datetime.now() 设置为已知值。为此,我使用 freezegun.freeze_time
:
def test_quarter_results_date(self):
with freeze_time("2012-01-14"):
print('check datetime.now()', datetime.now())
c = Company.objects.create()
...
然而,尽管 print
语句显示 2012-01-14
,日期时间并未冻结,因为它在评估 c1.q1_results_date
时仍然使用今天的日期。
我该如何纠正?
这不起作用的原因是因为您调用了函数。因此,这意味着 datetime
在 class 被解释时被评估,所以这基本上是在您启动服务器时。在那一刻冻结枪还没有激活。
这也意味着,如果您稍后 运行 服务器运行了很长时间,并且年份增加了,它仍将使用旧值。
您可以将可调用对象传递给默认值,从而使用辅助函数,例如:
def quarter_results_date(month):
yr = datetime.datetime.now().year
__, dy = calendar.monthrange(yr, month)
return datetime.datetime(
yr,
month,
dy
)
def <b>quarter_results_date_first</b>():
return quarter_results_date(1)
class Company(models.Model):
q1_results_date = models.DateField(
verbose_name='Q1 financial results',
default=<b>quarter_results_date_first</b>,
blank=False,
null=False,
)
请注意,default=quarter_results_date_first
没有使用括号,因此我们将引用传递给函数,而不是日期时间值。