前回と同じくPythonのUTに関するメモ書き。 datetime.now(現在日時)を利用したコードの単体テストの方法についてまとめます。

まずは、サンプルコード。現在日時を取得するメソッドです。

import datetime

def echo_current_time():
    return 'current time: {0}'.format(
        datetime.datetime.now().strftime('%Y-%m-%d')
    )

if __name__ == '__main__':
    print(echo_current_time())

以下は、UTコードになります。実行時間によって結果が変わるので、datetimeにモックを当てたくなりますが、下記の通りエラーになります。

from mock import patch
from nose.tools import eq_, nottest

@patch('datetime.datetime.strftime')
def test_echo_current_time(m_date):
    m_date.return_value = '2015-02-25'
    eq_('current time: 2015-02-25', sample.echo_current_time())
$ nosetests
E.
======================================================================
ERROR: test_sample.test_echo_current_time
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/sbkro/.virtualenvs/sample/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
    self.test(*self.arg)
  File "/Users/sbkro/.virtualenvs/sample/lib/python2.7/site-packages/mock.py", line 1214, in patched
    patching.__exit__(*exc_info)
  File "/Users/sbkro/.virtualenvs/sample/lib/python2.7/site-packages/mock.py", line 1381, in __exit__
    delattr(self.target, self.attribute)
TypeError: can't set attributes of built-in/extension type 'datetime.datetime'

----------------------------------------------------------------------
Ran 2 tests in 0.044s

FAILED (errors=1)

このようなケースの場合、mockではなく、freezegunを利用するとよいです。freezegunは、指定の日時に、datetime.nowの結果を固定します。

インストールは、pipから。

$ pip install freezegun

使い方は、モックを当てたいメソッドに、freeze_timeデコレータをラップします。 引数には、固定させたい日付を指定します。

@freeze_time('2015-07-16 12:23:34')
def test_echo_current_time():
    eq_('current time: 2015-07-16', sample.echo_current_time())
$ nosetests
.
-----------------------------------------
Ran 1 test in 0.057s

OK

無事動きました。

動作環境

  • OSX 10.9.5
  • Python 2.7.5
  • freezegun 0.2.8


Comments

comments powered by Disqus