如何使用模拟来测试next_day_of_week函数

时间:2011-07-04 20:10:07

标签: python django unit-testing

我正在跟踪在一周中的某一天(例如,该月的第一个星期日,该月的第三个星期五)重复发生的事件。我有一个DayOfWeek模型,用于存储活动的一周中的某一天。它包含一个方法next_day_of_week,用于将日期对象集返回到给定事件实例设置为的任何工作日的下一次出现(这有助于确定下一次出现事件的时间)。

例如,2011年7月3日星期日:

  • 对于DayOfWeek设置为星期日的对象,next_day_of_week将返回2011年7月3日。
  • 对于DayOfWeek设置为星期一,它将返回2011年7月4日。
  • 对于DayOfWeek设置为星期六,它将返回7/9/2011。

等等。我正在编写单元测试(我有史以来第一次;我提到过我对这些东西很新吗?)并且试图围绕如何测试这种方法。我知道我需要嘲笑一些东西,但我不太清楚是什么。这个问题似乎得到了我的要求:Python: Trying to mock datetime.date.today() but not working

所以我尝试在tests.py中模拟出datetime.date:

class FakeDate(date):
"A fake replacement for date that can be mocked for testing."
    def __new__(cls, *args, **kwargs):
        return date.__new__(date, *args, **kwargs)

我创建了我的测试用例,在模拟类中修补并设置为今天7/3/2011:

class TestDayOfWeek(TestCase):
    """Test the day of the week functions."""

    @mock.patch('datetime.date', FakeDate)
    def test_valid_my_next_day_of_week_sameday(self):
        from datetime import date
        FakeDate.today = classmethod(lambda cls: date(2011, 7, 3)) # July 3, 2011 is a Sunday
        new_day_of_week = DayOfWeek.objects.create()
        new_day_of_week.day = "SU"
    self.assertEquals(new_day_of_week.my_next_day_of_week(), date(2011, 7, 3))

供参考,这是模型类:

class DayOfWeek(ModelBase):
"""
Represents a day of the week (on which an event can take place). 
Because the dates of these events are often designated by terms like 'first Monday'
or 'third Friday', this field is useful in determining on which dates individual
readings take place.
"""

# The pk in the db is 1-indexed (Monday=1, Tuesday=2, etc), but python's days 
# of the week are 0-indexed if you use .weekday(), so we are using .isoweekday()
# instead. This list is used in my_next_day_of_week.
days =[ 'No day', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday',
       'Sunday' ]

DAYS_OF_WEEK_CHOICES = (
('MO', days[1]),
('TU', days[2]),
('WE', days[3]),
('TH', days[4]),
('FR', days[5]),
('SA', days[6]),
('SU', days[7]),
)

day = models.CharField(max_length=2, choices=DAYS_OF_WEEK_CHOICES)

def __unicode__(self):
    for daypair in self.DAYS_OF_WEEK_CHOICES:
        if self.day in daypair:
            return daypair[1]

    # This shouldn't happen
    raise InvalidDayOfWeekError

# my_next_day_of_week returns a datetime equal to the start (midnight+min) of the next day that is this instance's day of the week.
# It doesn't know what time the event is, so if today is the day of the week the event falls on,
# it simply returns today.
def my_next_day_of_week(self):
    """ 
    Returns a datetime equal to the start of the next day that is this instance's day of the week. 
    """

    today_day = date.today().isoweekday() # Find the number of the current day of the week
    reading_day = self.days.index(self.__unicode__()) # Find the number of the instance's day of the week
            # There is probably a more pythonic way to do this next part
    next_day = date.today() # start with next day = today
    while next_day.isoweekday() != reading_day:
        next_day = next_day + timedelta(1)

    return next_day

因此,当我运行django的测试运行器时,测试失败,因为我的DayOfWeek实例似乎没有使用模拟datetime.date而是看到今天的实际日期。从我的阅读中,我了解到模拟只存在于测试方法中,而不是之前或之后。但这是否意味着它不存在从测试方法中实例化/调用的任何对象/方法?那有什么用呢?我不认为这是问题,而是在修补时我做错了。可能是命名空间的问题?我正在读这个:http://www.voidspace.org.uk/python/mock/patch.html#id2 我将继续尝试修复它,并且如果我成功将编辑它,但在此之前任何指针都会受到赞赏!

编辑:意识到我在我的模型中使用datetime.datetime而不是datetime.date。我修复了它并编辑了上面的代码,但是未使用的模拟类的基本问题仍然存在。

2 个答案:

答案 0 :(得分:10)

想出来。

问题确实是在哪里修补,答案来自于研究我上面(this page)链接的Mock文档。

解决方案是模拟包含此应用模型的模块的命名空间中的date类,如下所示:

class TestDayOfWeek(TestCase):
    #Test the day of the week functions.

    # mock out the date class in the module that has already imported it via
    # from datetime import date, i.e. series.models (app_name.module_name)
    @mock.patch('series.models.date', FakeDate)
    def test_valid_my_next_day_of_week_sameday(self):
        from datetime import date
        FakeDate.today = classmethod(lambda cls: date(2011, 7, 3)) # July 3, 2011 is a Sunday

        new_day_of_week = DayOfWeek.objects.create()
        new_day_of_week.day = "SU"
        self.assertEquals(new_day_of_week.my_next_day_of_week(), date(2011, 7, 3))

希望这对其他人有帮助!

答案 1 :(得分:0)

我认为您需要的答案包含在the other post中。我已经改写以满足您的需求。

import datetime
class FakeDate(datetime.date):
    @classmethod
    def today(cls):
        return cls(2011, 7, 3)
datetime.date = FakeDate

class TestDayOfWeek(TestCase):
    """Test the day of the week functions."""

    def test_valid_my_next_day_of_week_sameday(self):
        new_day_of_week = DayOfWeek.objects.create()
        new_day_of_week.day = "SU"
        self.assertEquals(new_day_of_week.my_next_day_of_week(), date(2011, 7, 3))