web-dev-qa-db-ja.com

ユニットテスト用のクラスメソッドでpythonのdatetime.now()をモックする方法は?

次のようなメソッドを持つクラスのテストを記述しようとしています。

_import datetime
import pytz

class MyClass:
    def get_now(self, timezone):
        return datetime.datetime.now(timezone)

    def do_many_things(self, tz_string='Europe/London'):
        tz = pytz.timezone(tz_string)
        localtime_now = self.get_now(tz)
        ...
        return things
_

私はそれをテストしたいので、datetime.datetime.now()呼び出しが予測可能なものを返すことを確認する必要があります。

私はテストで Mock を使用する多くの例を読んでいますが、必要なものがまったく見つからず、テストでそれを使用する方法を見つけることができません。

get_now()ではなくdatetime.datetime.now()メソッドをモックしやすいように分離しましたが、それでも困惑しています。 Mockを使用してこのためのUnitTestsを作成する方法についての考えはありますか? (これはすべてDjangoのfwiwにあります。これがこの場合に違いをもたらすかどうかはわかりません。)

38
Phil Gyford

渡されたタイムゾーンにローカライズされた特定の日時を返す関数を作成します。

import mock

def mocked_get_now(timezone):
    dt = datetime.datetime(2012, 1, 1, 10, 10, 10)
    return timezone.localize(dt)

@mock.patch('path.to.your.models.MyClass.get_now', side_effect=mocked_get_now)
def your_test(self, mock_obj):
    # Within this test, `MyClass.get_now()` is a mock that'll return a predictable
    # timezone-aware datetime object, set to 2012-01-01 10:10:10.

そうすれば、結果のタイムゾーン対応の日時が正しく処理されているかどうかをテストできます。他の場所の結果は正しいタイムゾーンを表示するはずですが、予測可能な日付と時刻になります。

mocked_get_nowをモックするときの副作用として、get_now関数を使用します。コードがget_nowを呼び出すたびに、その呼び出しはmockおよびによって記録され、mocked_get_nowが呼び出され、その戻り値がget_nowの呼び出し元。

25
Martijn Pieters

freezegun を使用できます:

from freezegun import freeze_time

def test():
    assert datetime.datetime.now() != datetime.datetime(2012, 1, 14)
    with freeze_time("2012-01-14"):
        assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)
    assert datetime.datetime.now() != datetime.datetime(2012, 1, 14)

基本的にdatetimeモジュール呼び出しをモックします。

37

私はdateを使用していますが、同じ考え方がdatetimeでも機能するはずです。

class SpoofDate(date):
    def __new__(cls, *args, **kwargs):
        return date.__new__(date, *args, **kwargs)

...

from mock import patch

@patch('some.module.date', SpoofDate)
def testSomething(self):
    SpoofDate.today = classmethod(lambda cls : date(2012, 9, 24))

どこ some.moduleインポートdate。パッチは、インポートされたdateSpoofDateに置き換えます。これを再定義して、好きなようにすることができます。

4
Izkata

'testfixtures'パッケージのヘルパーを使用して、今呼び出している日時クラスをモックアウトします()。

http://packages.python.org/testfixtures/datetime.html#datetimes

このようにして、すべてのケースをいつでもテストできます。

2
Chris Withers

Unittest.mockのパッチを使用する

from unittest.mock import patch

@patch('MyClass.datetime')
def test_foo(self, mock_datetime):
    mock_datetime.datetime.now.return_value = datetime.datetime(2019, 5, 7) #SOME_MOCKED_DATE

クラスにのみインポートされるdatetimeモジュールをオーバーライドしています

テストを作成するクラス:

import datetime

class MyClass:
    def foo():
       localtime_now = datetime.datetime.now(timezone)

モックしやすくするためだけにget_now()メソッドとして分離する必要はありません。

0
Riya John