Mock datetime.datetime.now () [duplicate]

Ссылка NullReferenceException или Object, не установленная на экземпляр объекта, возникает, когда объект класса, который вы пытаетесь использовать, не создается. Например:

Предположим, что у вас есть класс с именем Student.

public class Student
{
    private string FirstName;
    private string LastName;
    public string GetFullName()
    {
        return FirstName + LastName;
    }
}

Теперь рассмотрим другой класс, в котором вы пытаетесь получить полное имя учащегося.

public class StudentInfo
{      
    public string GetStudentName()
    {
        Student s;
        string fullname = s.GetFullName();
        return fullname;
    }        
}

Как видно из вышеприведенного кода, оператор Student s - объявляет только переменную типа Student, обратите внимание, что класс Student не создается в этой точке. Следовательно, когда выполняется выполнение инструкции s.GetFullName (), она выкинет исключение NullReferenceException.

95
задан Belmin Fernandez 19 December 2010 в 07:54
поделиться

11 ответов

Есть несколько проблем.

Прежде всего, способ, которым вы используете mock.patch, не совсем прав. При использовании в качестве декоратора он заменяет данную функцию / класс (в данном случае datetime.date.today) объектом Mock только внутри декорированной функции . Таким образом, только в пределах вашей today() будет datetime.date.today быть другой функцией, которая, кажется, не является тем, что вы хотите.

То, что вы действительно хотите, похоже, больше похоже на это:

@mock.patch('datetime.date.today')
def test():
    datetime.date.today.return_value = date(2010, 1, 1)
    print datetime.date.today()

К сожалению, это не сработает:

>>> test()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "build/bdist.macosx-10.6-universal/egg/mock.py", line 557, in patched
  File "build/bdist.macosx-10.6-universal/egg/mock.py", line 620, in __enter__
TypeError: can't set attributes of built-in/extension type 'datetime.date'

Это не работает, потому что встроенные типы Python неизменяемы - см. этот ответ для более подробной информации.

В этом случае я буду подклассифицировать datetime.date самостоятельно и создать правильную функцию:

import datetime
class NewDate(datetime.date):
    @classmethod
    def today(cls):
        return cls(2010, 1, 1)
datetime.date = NewDate

И теперь вы можете сделать:

>>> datetime.date.today()
NewDate(2010, 1, 1)
86
ответ дан Community 25 August 2018 в 19:43
поделиться

Вы можете использовать следующий подход, основанный на решении Daniel G. У этого есть преимущество не нарушать проверку типа с помощью isinstance(d, datetime.date).

import mock

def fixed_today(today):
    from datetime import date

    class FakeDateType(type):
        def __instancecheck__(self, instance):
            return isinstance(instance, date)

    class FakeDate(date):
        __metaclass__ = FakeDateType

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

        @staticmethod
        def today():
            return today

    return mock.patch("datetime.date", FakeDate)

В основном мы заменяем класс на основе C datetime.date нашим собственным подклассом python, который создает оригинальные экземпляры datetime.date и отвечает к isinstance() запросам точно как native datetime.date.

Используйте его в качестве менеджера контекста в своих тестах:

with fixed_today(datetime.date(2013, 11, 22)):
    # run the code under test
    # note, that these type checks will not break when patch is active:
    assert isinstance(datetime.date.today(), datetime.date)

Аналогичный подход может использоваться для издевательства функции datetime.datetime.now().

5
ответ дан Andrey Lebedev 25 August 2018 в 19:43
поделиться

Я применил метод @ user3016183 с помощью пользовательского декоратора:

def changeNow(func, newNow = datetime(2015, 11, 23, 12, 00, 00)):
    """decorator used to change datetime.datetime.now() in the tested function."""
    def retfunc(self):                             
        with mock.patch('mymodule.datetime') as mock_date:                         
            mock_date.now.return_value = newNow
            mock_date.side_effect = lambda *args, **kw: datetime(*args, **kw)
            func(self)
    return retfunc

Я думал, что однажды может помочь кому-то ...

0
ответ дан DainDwarf 25 August 2018 в 19:43
поделиться

Несколько решений обсуждаются в http://blog.xelnor.net/python-mocking-datetime/ . В целом:

Mock object - Простой и эффективный, но прерывает проверку isinstance ():

target = datetime.datetime(2009, 1, 1)
with mock.patch.object(datetime, 'datetime', mock.Mock(wraps=datetime.datetime)) as patched:
    patched.now.return_value = target
    print(datetime.datetime.now())

Класс Mock

import datetime
import mock

real_datetime_class = datetime.datetime

def mock_datetime_now(target, dt):
    class DatetimeSubclassMeta(type):
        @classmethod
        def __instancecheck__(mcs, obj):
            return isinstance(obj, real_datetime_class)

    class BaseMockedDatetime(real_datetime_class):
        @classmethod
        def now(cls, tz=None):
            return target.replace(tzinfo=tz)

        @classmethod
        def utcnow(cls):
            return target

    # Python2 & Python3 compatible metaclass
    MockedDatetime = DatetimeSubclassMeta('datetime', (BaseMockedDatetime,), {})

    return mock.patch.object(dt, 'datetime', MockedDatetime)

Использовать как:

with mock_datetime_now(target, datetime):
   ....
1
ответ дан eddygeek 25 August 2018 в 19:43
поделиться

Для чего это стоит, Mock docs говорит о datetime.date.today специально, и это возможно сделать, не создавая фиктивный класс:

http: // www. voidspace.org.uk/python/mock/examples.html#partial-mocking

>>> from datetime import date
>>> with patch('mymodule.date') as mock_date:
...     mock_date.today.return_value = date(2010, 10, 8)
...     mock_date.side_effect = lambda *args, **kw: date(*args, **kw)
...
...     assert mymodule.date.today() == date(2010, 10, 8)
...     assert mymodule.date(2009, 6, 8) == date(2009, 6, 8)
...
66
ответ дан ForeverWintr 25 August 2018 в 19:43
поделиться

Я столкнулся с такой же ситуацией пару дней назад, и мое решение состояло в том, чтобы определить функцию в модуле для тестирования и просто издеваться над этим:

def get_date_now():
    return datetime.datetime.now()

Сегодня я узнал о FreezeGun , и, похоже, он красиво покрывает этот случай

from freezegun import freeze_time
import datetime
import unittest


@freeze_time("2012-01-14")
def test():
    assert datetime.datetime.now() == datetime.datetime(2012, 1, 14)
1
ответ дан Hito_kun 25 August 2018 в 19:43
поделиться

Наверное, я немного опоздал на это, но я думаю, что основная проблема заключается в том, что вы исправляете файл datetime.date.today напрямую и, согласно документации, это неправильно.

Вы следует исправить ссылку, импортированную в файл, где, например, тестируется функция.

Допустим, у вас есть файл functions.py, где у вас есть следующее:

import datetime

def get_today():
    return datetime.date.today()

then , в вашем тесте вы должны иметь что-то вроде этого

import datetime
import unittest

from functions import get_today
from mock import patch, Mock

class GetTodayTest(unittest.TestCase):

    @patch('functions.datetime')
    def test_get_today(self, datetime_mock):
        datetime_mock.date.today = Mock(return_value=datetime.strptime('Jun 1 2005', '%b %d %Y'))
        value = get_today()
        # then assert your thing...

Надеюсь, это немного поможет.

26
ответ дан israelord 25 August 2018 в 19:43
поделиться

Возможно, вы можете использовать свой собственный метод «today ()», который вы будете исправлять там, где это необходимо. Пример с mocking utcnow () можно найти здесь: https://bitbucket.org/k_bx/blog/src/tip/source/en_posts/2012-07-13-double-call-hack.rst?at= по умолчанию

0
ответ дан Jens Timmerman 25 August 2018 в 19:43
поделиться

Вообще говоря, вы бы datetime или, возможно, datetime.date импортировали в модуль где-нибудь. Более эффективным способом издеваться над этим методом было бы его исправление на модуле, который импортирует его. Пример:

a.py

from datetime import date

def my_method():
    return date.today()

Затем для вашего теста сам макет-объект будет передан в качестве аргумента методу тестирования. Вы должны настроить макет с требуемым значением результата, а затем вызвать ваш метод под тестированием. Тогда вы утверждаете, что ваш метод сделал то, что вы хотите.

>>> import mock
>>> import a
>>> @mock.patch('a.date')
... def test_my_method(date_mock):
...     date_mock.today.return_value = mock.sentinel.today
...     result = a.my_method()
...     print result
...     date_mock.today.assert_called_once_with()
...     assert mock.sentinel.today == result
...
>>> test_my_method()
sentinel.today

Слово предупреждения. Конечно, можно смеяться от насмешек. Когда вы это делаете, это делает ваши тесты более продолжительными, трудными для понимания и невозможными для поддержания. Прежде чем вы издеваетесь над таким простым методом, как datetime.date.today, спросите себя, действительно ли вам нужно , чтобы издеваться над ним. Если ваш тест является коротким и точным и отлично работает без насмешивания функции, вы можете просто посмотреть на внутреннюю деталь тестируемого кода, а не на объект, который вам нужно высмеять.

3
ответ дан jpmc26 25 August 2018 в 19:43
поделиться

Другой вариант - использовать https://github.com/spulec/freezegun/

Установить его:

pip install freezegun

И использовать его :

from freezegun import freeze_time

@freeze_time("2012-01-01")
def test_something():

    from datetime import datetime
    print(datetime.now()) #  2012-01-01 00:00:00

    from datetime import date
    print(date.today()) #  2012-01-01

Он также влияет на другие вызовы datetime в вызовах методов из других модулей:

other_module.py:

from datetime import datetime

def other_method():
    print(datetime.now())    

main.py:

from freezegun import freeze_time

@freeze_time("2012-01-01")
def test_something():

    import other_module
    other_module.other_method()

И, наконец:

$ python main.py
# 2012-01-01
113
ответ дан Mehdi Behrooz 25 August 2018 в 19:43
поделиться

Самый простой способ для меня:

from unittest import patch, Mock

def test():
    datetime_mock = Mock(wraps=datetime)
    datetime_mock.now = Mock(return_value=datetime(1999, 1, 1)
    patch('target_module.datetime', new=datetime_mock).start()

ВНИМАНИЕ для этого решения: вся функциональность из datetime module из target_module перестанет работать.

27
ответ дан Nam G VU 25 August 2018 в 19:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: