datetime.date.todayの結果をfreezegunを使って固定する

以前freezegunというライブラリを紹介しました
http://d.hatena.ne.jp/yuheiomori0718/20121216/1355663947


昨日エントリ書いたdatetime.dateを固定するっていうのは、freezegunを使うと以下のように書けます。

import datetime
from unittest import TestCase
from dateutil.relativedelta import relativedelta
from freezegun import freeze_time


class DateTodayMockTest(TestCase):

    @freeze_time('2014-1-1')
    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, 3)


このコード、python -m unittestで動かすと成功するんですが、Pycharmから動かすと失敗しました。

Traceback (most recent call last):
  File "/Applications/PyCharm 2.7 EAP.app/helpers/pycharm/utrunner.py", line 152, in <module>
    TeamcityTestRunner().run(all, **options)
  File "/Applications/PyCharm 2.7 EAP.app/helpers/pycharm/tcunittest.py", line 197, in run
    test(result)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/suite.py", line 65, in __call__
    return self.run(*args, **kwds)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/suite.py", line 103, in run
    test(result)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/case.py", line 376, in __call__
    return self.run(*args, **kwds)
  File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/unittest/case.py", line 355, in run
    result.stopTest(self)
  File "/Applications/PyCharm 2.7 EAP.app/helpers/pycharm/tcunittest.py", line 174, in stopTest
    d = datetime.datetime.now() - start
  File "/Users/yuhei/.virtualenvs/sandbox/lib/python2.7/site-packages/freezegun/api.py", line 56, in __sub__
    return datetime_to_fakedatetime(result)
  File "/Users/yuhei/.virtualenvs/sandbox/lib/python2.7/site-packages/freezegun/api.py", line 79, in datetime_to_fakedatetime
    return FakeDatetime(datetime.year,
AttributeError: 'datetime.timedelta' object has no attribute 'year'


Pycharmのutrunner内部でdatetimeの引き算をやっててそれがエラーになったみたいです。

freezegunのコード読んでみるとFakeDatetimeの__sub__でスーパークラスの減算の結果に対してこんな処理を書いてます

def datetime_to_fakedatetime(datetime):
    return FakeDatetime(datetime.year,
                        datetime.month,
                        datetime.day,
                        datetime.hour,
                        datetime.minute,
                        datetime.second,
                        datetime.microsecond,
                        datetime.tzinfo)


減算の結果はdatetimeでなくてtimedeltaですんで、上記のようなエラーになるんですね。

なのでpycharmのテストランナーは別に悪くなくて、こんなテストだとpycharm関係なく失敗になります。

from dateutil.relativedelta import relativedelta

import datetime
import unittest
from freezegun import freeze_time


class DateTodaySubtractionTest(unittest.TestCase):

    @freeze_time('2014-1-1')
    def test_sub(self):
        today = datetime.date.today()
        tomorrow = today + relativedelta(days=1)
        self.assertEquals(tomorrow - today, datetime.timedelta(1))


明確なバグなのでもう直ってるかなと思ったら、やっぱ直ってました。
https://github.com/spulec/freezegun/commit/4884bd0a6017536a46895bde5cd04f621d93e42d


githubからpip installしたらちゃんとpycharmから動かせるようになりました。

pip install git+git://github.com/spulec/freezegun.git