使用基于日期/时间的对象进行Django单元测试

发布于 2021-01-29 15:06:46

假设我有以下Event模型:

from django.db import models
import datetime

class Event(models.Model):
    date_start = models.DateField()
    date_end = models.DateField()

    def is_over(self):
        return datetime.date.today() > self.date_end

我想Event.is_over()通过创建一个在将来(今天+
1或某天)结束的事件进行测试,并对日期和时间进行打桩,以便系统认为我们已经达到了该将来的日期。

就python而言,我希望能够存根所有系统时间对象。这包括datetime.date.today()datetime.datetime.now()和任何其他标准日期/时间对象。

这样做的标准方法是什么?

关注者
0
被浏览
99
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

    编辑
    :由于我的答案是此处接受的答案,因此我正在对其进行更新,以使所有人都知道在此同时创建了更好的方法,这是freezegun库:https
    ://pypi.python.org/pypi/freezegun 。当我想影响测试时间时,会在所有项目中使用此功能。看看它。

    原始答案:

    更换内部零件总是很危险的,因为它可能会带来讨厌的副作用。因此,您真正想要的是让猴子修补尽可能地局部。

    我们使用了Michael Foord出色的模拟库:http
    ://www.voidspace.org.uk/python/mock/
    ,该库具有@patch装饰器,该装饰器修补某些功能,但是Monkey修补器仅位于测试功能的范围内,并且一切功能超出范围后会自动恢复。

    唯一的问题是内部datetime模块是用C实现的,因此默认情况下您将无法进行猴子补丁。我们通过制作 可以 模拟的自己的简单实现来解决此问题。

    总体解决方案是这样的(示例是在Django项目中使用的验证器函数,用于验证日期在将来)。请注意,我是从一个项目中拿出来的,但是拿出了一些不重要的东西,因此在复制粘贴时,事情可能实际上不起作用,但是我希望您明白了,

    首先,我们datetime.date.today在一个名为的文件中定义自己的非常简单的实现utils/date.py

    import datetime
    
    def today():
        return datetime.date.today()
    

    然后,我们在以下代码中为此验证器创建单元测试tests.py

    import datetime
    import mock
    from unittest2 import TestCase
    
    from django.core.exceptions import ValidationError
    
    from .. import validators
    
    class ValidationTests(TestCase):
        @mock.patch('utils.date.today')
        def test_validate_future_date(self, today_mock):
            # Pin python's today to returning the same date
            # always so we can actually keep on unit testing in the future :)
            today_mock.return_value = datetime.date(2010, 1, 1)
    
            # A future date should work
            validators.validate_future_date(datetime.date(2010, 1, 2))
    
            # The mocked today's date should fail
            with self.assertRaises(ValidationError) as e:
                validators.validate_future_date(datetime.date(2010, 1, 1))
            self.assertEquals([u'Date should be in the future.'], e.exception.messages)
    
            # Date in the past should also fail
            with self.assertRaises(ValidationError) as e:
                validators.validate_future_date(datetime.date(2009, 12, 31))
            self.assertEquals([u'Date should be in the future.'], e.exception.messages)
    

    最终的实现如下所示:

    from django.utils.translation import ugettext_lazy as _
    from django.core.exceptions import ValidationError
    
    from utils import date
    
    def validate_future_date(value):
        if value <= date.today():
            raise ValidationError(_('Date should be in the future.'))
    

    希望这可以帮助



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看