简体   繁体   English

如何冻结 datetime.now 进行单元测试

[英]How to freeze datetime.now for unit testing

I have a model that uses a function to return a default datetime:我有一个 model 使用 function 返回默认日期时间:

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]
    )

I want to unit test this, which requires me to set datetime.now() to a known value.我想对此进行单元测试,这需要我将 datetime.now() 设置为已知值。 To do this I am using freezegun.freeze_time :为此,我使用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()
    ...

However, although the print statement shows 2012-01-14 , the datetime is not frozen as it still uses today's date when evaluating c1.q1_results_date .但是,尽管print语句显示2012-01-14 ,但日期时间并未冻结,因为它在评估c1.q1_results_date时仍使用今天的日期。

How can I correct this?我该如何纠正?

The reason that this will not work is because you call the function.这不起作用的原因是因为您调用了 function。 This thus means the datetime is evaluated when the class is interpreted, so this is basically when you start the server.因此,这意味着在解释datetime时会评估日期时间,所以这基本上是在您启动服务器时。 At that moment the freezegun is not yet active.在那一刻,冷冻枪尚未激活。

This thus also means that if you later run the server for some long time, and the year gets incremented, it will still use the old value.因此,这也意味着,如果您稍后运行服务器一段时间,并且年份增加,它仍将使用旧值。

You can pass a callable to the default value, and thus use a helper function for example:您可以将可调用对象传递给默认值,从而使用帮助程序 function 例如:

def quarter_results_date(month):
    yr = datetime.datetime.now().year
    __, dy = calendar.monthrange(yr, month)
    return datetime.datetime(
        yr,
        month,
        dy
    )

def quarter_results_date_first():
    return quarter_results_date(1)

class Company(models.Model):
    q1_results_date = models.DateField(
        verbose_name='Q1 financial results',
        default=quarter_results_date_first,
        blank=False,
        null=False,
    )

Note that no parenthesis are used for the default=quarter_results_date_first , so we pass a reference to the function, not a datetime value.请注意, default=quarter_results_date_first没有使用括号,因此我们传递了对 function 的引用,而不是日期时间值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM