はじめに
パッケージに制約がある中でdatetimeを含むテストを書くときに困ったのでメモがてら書いておきます
環境
Python:3.9.7
pytest:2.7.0
pytest.mock:3.10.0
テスト対象のメソッド
import datetime
def return_now():
return datetime.now()
freezegunなしでテストを書いてみよう
「おりゃ」
import datetime
class Test_return_now:
def test_return_now(self, mocker):
mocker.patch.object(datetime, "now", return_value=datetime.datetime(1111, 12, 31, 12, 34, 56))
assert datetime_wrapper.GetFormattedCurrentTime() == "1111-12-31 12:34:56"
(そもそもこのテストをやる意義は一旦置いといて下さい...)
datetimeのnowメソッドがモックされ、return_valueが固定された為一見問題なさそうに見えます。
が、通りません。
datetimeモジュールとモジュール内で定義されたdatetimeの名称領域を両方モックしてしまうためreturn_valueのdatetimeもモックされてしまいAttributeErrorが出ます。
これは、datetime_wrapper.py(test対象のモジュール)内のdatetimeのみをモックすることで回避可能です。
例を以下に示します。
- 良いパターン
import datetime
class Test_return_now:
def test_return_now(self, mocker):
mock_now = mocker.patch.object(datetime_wrapper, "datetime").datetime.now
mock_now.return_value = datetime.datetime(1111, 12, 31, 12, 34, 56)
assert datetime_wrapper.return_now() == "1111-12-31 12:34:56"
以上のようにすることでピンポイントにdatetime_wrapper内のdatetimeのみをモックすることができるためエラーを吐かずにテストを実行できます。
この例はオブジェクト単位でモックができるmocker.objectを使う上で汎用性のある考え方で、その仕組みの理解にもつながるので覚えといて損はないと思います。