Ссылка 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.
Есть несколько проблем.
Прежде всего, способ, которым вы используете 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)
Вы можете использовать следующий подход, основанный на решении 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()
.
Я применил метод @ 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
Я думал, что однажды может помочь кому-то ...
Несколько решений обсуждаются в 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):
....
Для чего это стоит, 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)
...
Я столкнулся с такой же ситуацией пару дней назад, и мое решение состояло в том, чтобы определить функцию в модуле для тестирования и просто издеваться над этим:
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)
Наверное, я немного опоздал на это, но я думаю, что основная проблема заключается в том, что вы исправляете файл 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...
Надеюсь, это немного поможет.
Возможно, вы можете использовать свой собственный метод «today ()», который вы будете исправлять там, где это необходимо. Пример с mocking utcnow () можно найти здесь: https://bitbucket.org/k_bx/blog/src/tip/source/en_posts/2012-07-13-double-call-hack.rst?at= по умолчанию
Вообще говоря, вы бы 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
, спросите себя, действительно ли вам нужно , чтобы издеваться над ним. Если ваш тест является коротким и точным и отлично работает без насмешивания функции, вы можете просто посмотреть на внутреннюю деталь тестируемого кода, а не на объект, который вам нужно высмеять.
Другой вариант - использовать 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
Самый простой способ для меня:
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
перестанет работать.