isinstance и Mocking

class HelloWorld(object): def say_it(self): return 'Hello I am Hello World' def i_call_hello_world(hw_obj): print 'here... check type: %s' %type(HelloWorld) if isinstance(hw_obj, HelloWorld): print hw_obj.say_it() from mock import patch, MagicMock import unittest class TestInstance(unittest.TestCase): @patch('__main__.HelloWorld', spec=HelloWorld) def test_mock(self,MK): print type(MK) MK.say_it.return_value = 'I am fake' v = i_call_hello_world(MK) print v if __name__ == '__main__': c = HelloWorld() i_call_hello_world(c) print isinstance(c, HelloWorld) unittest.main() 

Вот трассировка

 here... check type: <type 'type'> Hello I am Hello World True <class 'mock.MagicMock'> here... check type: <class 'mock.MagicMock'> E ====================================================================== ERROR: test_mock (__main__.TestInstance) ---------------------------------------------------------------------- Traceback (most recent call last): File "/usr/local/lib/python2.7/dist-packages/mock.py", line 1224, in patched return func(*args, **keywargs) File "t.py", line 18, in test_mock v = i_call_hello_world(MK) File "t.py", line 7, in i_call_hello_world if isinstance(hw_obj, HelloWorld): TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types ---------------------------------------------------------------------- Ran 1 test in 0.002s 

Q1. Почему эта ошибка возникает? Они <class type='MagicMock>

Q2. Как приостановить насмешку, чтобы первая строка прошла, если ошибка исправлена?

Из документа

Обычно атрибут класса объекта возвращает его тип. Для макетного объекта с классом spec вместо этого возвращает класс spec. Это позволяет макетным объектам проходить тесты isststance для объекта, который они заменяют / маскируют как:

 mock = Mock(spec=3) isinstance(mock, int) True 

благодаря

4 Solutions collect form web for “isinstance и Mocking”

Не используйте isinstance , вместо этого проверяйте существование метода say_it . Если метод существует, назовите его:

 if hasattr(hw_obj, 'say_it'): print hw_obj.say_it() 

В любом случае, это лучший дизайн: использование информации о типе гораздо более хрупкое.

ИМХО, это хороший вопрос и говорит: « Не используйте isinstance , используйте вместо утиного набора текста » – это плохой ответ. Дайка печатается отлично, но не серебряная пуля. Иногда необходимо, даже если оно не является питоническим. Например, если вы работаете с какой-либо библиотекой или устаревшим кодом, который не является питоническим, вы должны играть с isinstance . Это просто реальный мир, и макет был разработан таким образом, чтобы соответствовать этой работе.

В коде большая ошибка – когда вы пишете:

 @patch('__main__.HelloWorld', spec=HelloWorld) def test_mock(self,MK): 

Из документации patch мы читаем (подчеркиваем, является моей):

Внутри тела функции или с выражением цель залатан новым объектом .

Это означает, что при установке объекта класса HelloWorld ссылка на HelloWorld будет заменена объектом MagicMock для контекста функции test_mock() .

Затем, когда i_call_hello_world() выполняется в if isinstance(hw_obj, HelloWorld): HelloWorld является MagicMock() а не классом (как показывает ошибка).

Это поведение связано с тем, что в качестве побочного эффекта исправления ссылки на класс второй isinstance(hw_obj, HelloWorld) становится объектом (экземпляр MagicMock ). Это не class или не type . Простым экспериментом для понимания этого поведения является изменение i_call_hello_world() следующим образом:

 HelloWorld_cache = HelloWorld def i_call_hello_world(hw_obj): print 'here... check type: %s' %type(HelloWorld_cache) if isinstance(hw_obj, HelloWorld_cache): print hw_obj.say_it() 

Ошибка исчезнет, ​​потому что исходная ссылка на класс HelloWorld сохраняется в HelloWorld_cache при загрузке модуля. Когда применяется патч, он изменит только HelloWorld а не HelloWorld_cache .

К сожалению, предыдущий эксперимент не дает нам возможности играть в таких случаях, как ваш, потому что вы не можете изменить библиотеку или устаревший код, чтобы ввести трюк, подобный этому. Более того, это те трюки, которые мы бы никогда не увидели в нашем коде.

Хорошей новостью является то, что вы можете что-то сделать, но вы не можете просто patch ссылку HelloWord в модуле, где у вас есть isinstace(o,HelloWord) для тестирования. Лучший способ зависит от реального случая, который вы должны решить. В вашем примере вы можете просто создать Mock для использования в качестве объекта HelloWorld , использовать аргумент spec чтобы одеть его как экземпляр HelloWorld и передать тест isinstance . Это точно одна из целей, для которых разработана spec . Ваш тест будет написан следующим образом:

 def test_mock(self): MK = MagicMock(spec=HelloWorld) #The hw_obj passed to i_call_hello_world print type(MK) MK.say_it.return_value = 'I am fake' v = i_call_hello_world(MK) print v 

И выход только части unittest

 <class 'mock.MagicMock'> here... check type: <type 'type'> I am fake None 

Мишель д'Амико дает правильный ответ на мой взгляд, и я настоятельно рекомендую его прочитать. Но это заняло у меня некоторое время, и, поскольку я уверен, что я вернусь к этому вопросу в будущем, я подумал, что минимальный пример кода поможет прояснить решение и дать быструю ссылку:

 from mock import patch, mock class Foo(object): pass # Cache the Foo class so it will be available for isinstance assert. FooCache = Foo with patch('__main__.Foo', spec=Foo): foo = Foo() assert isinstance(foo, FooCache) assert isinstance(foo, mock.mock.NonCallableMagicMock) # This will cause error from question: # TypeError: isinstance() arg 2 must be a class, type, or tuple of classes and types assert isinstance(foo, Foo) 

Я занимался этим сам в последнее время, когда писал некоторые модульные тесты. Одно из потенциальных решений – фактически не пытаться издеваться над всем классом HelloWorld, а вместо этого выкрикивать методы класса, которые вызывается тестируемым кодом. Например, что-то вроде этого должно работать:

 class HelloWorld(object): def say_it(self): return 'Hello I am Hello World' def i_call_hello_world(hw_obj): if isinstance(hw_obj, HelloWorld): return hw_obj.say_it() from mock import patch, MagicMock import unittest class TestInstance(unittest.TestCase): @patch.object(HelloWorld, 'say_it') def test_mock(self, mocked_say_it): mocked_say_it.return_value = 'I am fake' v = i_call_hello_world(HelloWorld()) self.assertEquals(v, 'I am fake') 
  • Как использовать аргумент командной строки в unittest?
  • Запускать nosetests с предупреждениями как ошибки?
  • повторное использование объекта в python doctest
  • Как я могу тестировать ответы из приложения Webapp WSGI в Google App Engine?
  • Создание и импорт вспомогательных функций в тестах без создания пакетов в тестовой директории с использованием py.test
  • Носетиты и комбинированное покрытие
  • Как я могу указать базу данных для использования тестов Django, а не создавать ее каждый раз?
  • Обнаружение теста Python с помощью доктрин, охвата и параллелизма
  • Python unittest игнорирует numpy
  • python mocking raw input в unittests
  • Предпочитаемая структура модульного тестирования Python
  •  
    Interesting Posts for Van-Lav

    Строка Python с пространством и без пространства в конце и неизменяемость

    Можно ли привязать аннотацию matplotlib к координате данных по оси x, но к относительному местоположению по оси y?

    Исходный код в списках пули с reStructuredText

    Данные веб-скрепок с использованием python?

    Popen () терпит неудачу с « дескриптор недействителен» в _cleanup () иногда

    Есть ли модуль Python для разбора нотации строк в исходной строке?

    Демистификация эффективности sharectypes

    Оповещения в Python?

    Как установить PyCairo (Cairo для Python) в Windows?

    Ubuntu 14.04: ImportError: ни один модуль с клиентом

    Библиотека текстовой текстуры Python – Как сохранить разрыв строки?

    Перезапустить python-скрипт изнутри

    Поворот Typeerror не может быть вызван. Я пытаюсь понять, почему мой оператор elif не может быть вызван

    В режиме emacs Python, как мне установить другую ширину автозаполнения для docstrings и кода?

    Когда я должен использовать классы в Python?

    Python - лучший язык программирования в мире.