3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

python単体テストでdatetimeをモック化する

Posted at

datetime.now()が返す値を固定化するのにハマったので、事象と解決策を残しておきます。

前提

  • 環境
    • python 3.8
    • ライブラリ
      • pytest
  • 以下のコードをテストする
sample.py
from datetime import datetime

def func():
    return datetime.now()

失敗例

一番初めに思いつくのがdatetime.now()に対してモックを当てることですが、これは失敗します。

test_sample.py
from datetime import datetime
from unittest import mock
from sample import func

@mock.patch('app.sample.datetime.now')
def test_func(m_now):
    m_now.return_value = datetime(2021, 2, 1)
    actual = func()
    assert actual == m_now()
テスト実行結果
...
E       TypeError: can't set attributes of built-in/extension type 'datetime.datetime'
...

datetime.datetimeの属性を勝手に変更しようとすんじゃねぇよ!!!!」と怒られました。

このようなエラーが発生する理由は、datetimeがイミュータブルなオブジェクトだからです。
イミュータブルなオブジェクトなので、メソッドを新しい関数(mock)に差し替えることができません。(参考:イミュータブルってなに?)

成功例

では、どうやってdatetime.now()の値を固定化すればいいのか。
これはdatetimeオブジェクトの中身を弄るのではなく、datetimeオブジェクトごと差し替えてあげることで解決できます。

test_sample.py
from datetime import datetime
from sample import func

@mock.patch('app.sample.datetime')
def test_func(m):
    m.now.return_value = datetime(2021, 2, 1)
    actual = func()
    assert actual == m.now()
テスト実行結果
...
================== 1 passed in 1.23s ==================

最後に

モック化するときは、対象のオブジェクトがイミュータブルかどうかは注意しておきたいですね。
ちなみにpytestが一つも出てきてないですが、裏でテストを実行してくれてました。
また、pytestでmockを使うならpytest-mockerなどを調べてみるのも面白いです。
では。

参考

まだmockで消耗してるの?mockを理解するための3つのポイント
Trying to mock datetime.date.today(), but not working|stack overflow

3
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?