pythonでdatetime.dateをpatchする

datetime.date.todayやdatetime.nowを使ったコードでテストのために日付を固定したいということがよくあるので、
datetime.dateをpatchする方法を試してみる

from unittest import TestCase
from dateutil.relativedelta import relativedelta
from mock import patch
import datetime


class DateTodayMockTest(TestCase):
    def setUp(self):
        class FakeDate(datetime.date):
            @classmethod
            def today(cls):
                return cls(2014, 1, 1)

        patcher = patch('datetime.date', FakeDate)
        self.addCleanup(patcher.stop)
        patcher.start()

    def test_today(self):
        today = datetime.date.today()
        self.assertEquals(today.year, 2014)
        self.assertEquals(today.month, 1)
        self.assertEquals(today.day, 1)
        tomorrow = today + relativedelta(days=1)

        self.assertEquals(tomorrow.year, 2014)
        self.assertEquals(tomorrow.month, 1)
        self.assertEquals(tomorrow.day, 2)

        day_after_day = tomorrow + relativedelta(days=1)
        self.assertEquals(day_after_day.year, 2014)
        self.assertEquals(day_after_day.month, 1)
        self.assertEquals(day_after_day.day, 12)


上記のようにpatchしたdatetimeを使ったテストでrelativedeltaを2回使うとエラーになった。

2度めのrelativedeltaで以下のエラーになる(day_after_day = tomorrow + relativedelta(days=1))

Traceback (most recent call last):
  File "date_today_mock_test.py", line 29, in test_today
    day_after_day = tomorrow + relativedelta(days=1)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/dateutil/relativedelta.py", line 247, in __radd__
    raise TypeError, "unsupported type for add operation"
TypeError: unsupported type for add operation


調べてみるとrelativedelta.__radd__の以下の箇所でエラーになっている

    def __radd__(self, other):
        if not isinstance(other, datetime.date):
            raise TypeError, "unsupported type for add operation"


tomorrowはdatetime.dateのオブジェクトなのになぜ?と最初思ったけど、よく考えたらdatetime.dateはpatchしてるので、otherがFakeDateのオブジェクトじゃないとエラーになるってことなんだろう。


一度目のrelativedeltaではtodayがFakeDateのオブジェクトなので上記の部分は通ったが、__radd__が通常のdateオブジェクトを返すので2度めではエラーになる。


isinstanceの挙動をオーバーライドできれば解決できそうだけど、続きはまた今度