LoginSignup
8
1

More than 1 year has passed since last update.

【pytest】pytestでfreezegunを使わずにdatetimeをモックしてみよう

Last updated at Posted at 2022-12-14

はじめに

パッケージに制約がある中でdatetimeを含むテストを書くときに困ったのでメモがてら書いておきます

環境

Python:3.9.7
pytest:2.7.0
pytest.mock:3.10.0

テスト対象のメソッド

datetime_wrapper.py
import datetime


def return_now():
    return datetime.now()

freezegunなしでテストを書いてみよう

「おりゃ」

test_datetime_wrapper.py
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"

(そもそもこのテストをやる意義は一旦置いといて下さい...)

datetimenowメソッドがモックされ、return_valueが固定された為一見問題なさそうに見えます。
が、通りません。

datetimeモジュールとモジュール内で定義されたdatetimeの名称領域を両方モックしてしまうためreturn_valuedatetimeもモックされてしまいAttributeErrorが出ます。

これは、datetime_wrapper.py(test対象のモジュール)内のdatetimeのみをモックすることで回避可能です。
例を以下に示します。

  • 良いパターン
test_datetime_wrapper.py
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を使う上で汎用性のある考え方で、その仕組みの理解にもつながるので覚えといて損はないと思います。

8
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
1