Исинстанция и издевательство

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. Как мне приостановить насмешку, чтобы первая строка прошла, если ошибка исправлена?

Отдоктор

Normally the class attribute of an object will return its type. For a mock object with a spec class returns the spec class instead. This allows mock objects to pass isinstance tests for the object they are replacing / masquerading as:

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

Спасибо

 CppLearner21 июн. 2012 г., 23:22
@MarkRansom Да, это зло. Но что является лучшим способом убедиться, что интерфейс, который мы передаем, является ПРАВИЛЬНЫМ?hasattr кажется, также не устраняет разрыв. Думаю, два объекта могут иметь одинаковые имена методов и использовать неправильный объект для успешного прохождения теста. Я думаю, что фокус вопроса сместился! Ааа.
 spinkus16 июл. 2016 г., 06:46
«Одна из многих приятных вещей в Python - это то, что он позволяет« Duck Typing ». где вы не заботитесь о точном типе объекта, если он выполняет то, что вы хотите & quot; - пока это не так.
 Mark Ransom21 июн. 2012 г., 23:12
Теперь вы знаете, почему использованиеisinstance обескуражен.
 CppLearner21 июн. 2012 г., 23:44
Спасибо Марк. Я думаю, что тема смещена. Но проблема с ошибкой еще не решена :( Я сделаю новый пост, когда подумаю над более конкретным вопросом в отношении экземпляра. Спасибо!
 Magnus Lie Hetland18 мар. 2013 г., 15:11
Вы получаете сообщение об ошибке, потому чтоHelloWorld (после исправления) не является классом или типом, ноmock.MagicMock пример. Как говорится в ошибке, вторым аргументом должен быть класс, тип или кортеж классов или типов.spec что вы обращаетесь к нам заfirst аргумент. Это то, что вы показываете в своем последнем примере (из документов). Почему именно вы хотите проверить,HelloWorld экземпляр является экземпляром эмулируемого типа (что, я думаю, невозможно)?

Ответы на вопрос(5)

don't use isinstance, use duck typing instead& Quot; плохой ответ Утка набирает отлично, но не серебряная пуля. Иногдаisinstance необходимо, даже если оно не питоническое. Например, если вы работаете с какой-то библиотекой или устаревшим кодом, который не является pythonic, вы должны поиграть с ним.isinstance, Это просто реальный мир, и макет был разработан, чтобы соответствовать такой работе.

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

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

Отpatch documentation мы читаем (подчеркиваю мое):

Inside the body of the function or with statement, the target is patched with a new object.

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

Тогда, когдаi_call_hello_world() выполнен вif isinstance(hw_obj, HelloWorld): HelloWorld этоMagicMock() объект, а не класс (как предполагает ошибка).

Такое поведение объясняется тем, что в качестве побочного эффекта исправления ссылки на класс 2-й аргумент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
 05 февр. 2015 г., 09:26
@seanazlin Я проверю это позже. Но почему об ошибке говорят, что HelloWord не является классом или типом? В любом случае, спасибо за ваш отзыв.
 05 февр. 2015 г., 10:47
@SeanAzlin Пожалуйста, введите в консоли Python следующие операторы:type(MagicMock()), type(MagicMock), type(object()), type(object), После этого, я надеюсь, вы поймете, что написанное мной правильно. Во всяком случае, если ваш комментарий о я писалMock вместоMagicMockЯ думаю, что это неправильно, но это не очень большая проблема ... Это просто деталь, которую я исправлю. Обратите больше внимания, когда вы используете downvote. Мой ответ правильный, и он единственный, который охватывает первоначальный вопрос без слов * Эй, парень! Не делайте этого ».
 04 февр. 2015 г., 23:09
Это неверное утверждение: & quot; HelloWorld - это объект Mock (), а не класс (как и предполагалось в сообщении об ошибке). & Quot; Если вы поймаете исходную ошибку TypeError и отладите ее, вы увидите, что выполнениеtype(HelloWorld) также будет возвращать & lt; class 'mock.MagicMock & gt ;.
 05 февр. 2015 г., 22:07
Я думаю, что я понимаю вашу точку зрения. type (HelloWorld) возвращает classobj, но type (MagicMock (spec = HelloWorld)) возвращает mock.MagicMock. При предыдущей отладке в контексте @ mock.patch я виделtype(HelloWorld) вернуть<class 'mock.MagicMock'>неmock.MagicMock, ОП видела то же самое. Ваше оригинальное решение, как написано, подразумевает, что вывод типа (объекта)<class 'mock.MagicMock'&g,t; означает, что объект будет разрешен как 2-й аргумент isinstance (), когда OP уже заявил, что это не так. Я все еще неправильно понимаю ваше решение?
 05 февр. 2015 г., 22:30
@ SeanAzlin Я имею в виду, что выходtype(MagicMock()) (так что типMegicMock object instance) точно<class 'mock.MagicMock'>, Во всяком случае, даже этоseams класс это не класс, а объект, когда вы пытаетесь использовать его в экземпляре. Эта неоднозначность устранена в Python 3, где isinstace простоtype или кортежtypes. В Python класс и объектные понятия действительно близки ... Когда вы используетеpatch Вы заменяете исходную ссылку новым объектом (MagicMock по умолчанию) событие, если это класс ... класс - это просто функции, которые возвращают объект.

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

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)
Решение Вопроса

isinstanceвместо проверки на наличиеsay_it метод. Если метод существует, вызовите его:

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

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

 CppLearner21 июн. 2012 г., 23:11
Благодарю. Но первый вопрос на самом деле: почему он выбрасывает эту ошибку? И во-вторых, принимая это изменение, если два объекта имеют одно и то же имя метода, тест пройден, верно?
 21 янв. 2016 г., 19:23
Downvote, предлагая не использоватьisinstance это ужасный ответ. Сделайте это правильно с Mock, очень похоже на это:stackoverflow.com/a/11283173/169153
 CppLearner21 июн. 2012 г., 23:19
Благодарю. Я перестану использовать isinstance после прочтения основного запасного варианта. Но еще ... еслиMyMomKitchenObject имеетsay_it и программист использует это в качестве входных данных для функции ... тест все равно будет проходить, не так ли? Итак, как мне проверить, действительно ли работает мой unittest? или как мне определить его правильность в моем коде? Так же, как и интеграционный тест, два объекта могут иметь 99% одинаковый интерфейс, и тестируемая система никогда не использует этот 1% разный, и тест все равно проходит, система будет «работать без проблем».
 21 июн. 2012 г., 23:15
Я не знаю, почему выдается ошибка. Это не имеет значения, скоро вы не будете использоватьisinstance :) И да, теперь вы можете передать любой объект, у которого есть метод и который ведет себя "правильно", и тест пройден.
 11 июн. 2014 г., 13:22
Не используйтеisinstance() это не решение для меня: я пытаюсь смоделировать datetime.datetime, и он используется во внешних библиотеках. В моем случаеdjango использует это.

MagicMock класс и переопределение__subclasscheck__ метод:

class BaseMagicMock(MagicMock):

    def __subclasscheck__(self, subclass):
        # I couldn't find another way to get the IDs
        self_id = re.search("id='(.+?)'", self.__repr__()).group(1)
        subclass_id = re.search("id='(.+?)'", subclass.__repr__()).group(1)
        return self_id == subclass_id

И тогда вы можете использовать этот класс с@patch декоратор:

class FooBarTestCase(TestCase):
    ...

    @patch('app.services.ClassB', new_callable=BaseMagicMock)
    @patch('app.services.ClassA', new_callable=BaseMagicMock)
    def test_mock_for_issubclass_support(self, ClassAMock, ClassBMock):
        check_for_subclasses(ClassAMock)

Вот и все!

Примечания:

ВыMUST издеватьсяall классы, которые сравниваются с помощьюissubclass.

Пример:

def check_for_subclasses(class_1):
    if issubclass(class_1, ClassA): # it's mocked above using BaseMagicMock
        print("This is Class A")
    if issubclass(class_1, ClassB): # it's mocked above using BaseMagicMock
        print("This is Class B")
    if issubclass(class_1, ClassC): # it's not mocked with @patch
        print("This is Class C")

issubclass(class_1, ClassC) вызовет ошибку {TypeError}issubclass() arg 1 must be a class так какClassC содержит значение по умолчанию__issubclass__ метод. И тогда мы должны обработать тест следующим образом:

class FooBarTestCase(TestCase):
    ...

    @patch('app.services.ClassC', new_callable=BaseMagicMock)
    @patch('app.services.ClassB', new_callable=BaseMagicMock)
    @patch('app.services.ClassA', new_callable=BaseMagicMock)
    def test_mock_for_issubclass_support(self, ClassAMock, ClassBMock):
        check_for_subclasses(ClassAMock)

когда писал некоторые юнит-тесты. Одно из возможных решений - на самом деле не пытаться макетировать весь класс 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')
 05 февр. 2015 г., 22:06
Эй .... Я знаю, что если вы нажмете стрелку вверх, которая станет не 0, а 1. В любом случае вы должны были поступить неправильно и исправить это ..... независимо от того, что это
 05 февр. 2015 г., 21:47
Что вы не можете сделать, когда у вас естьisinstance() Вызов просто исправьте класс, но вы можете использовать макет для объекта. Кстати: обратите внимание, когда вы используетеpatch.objectиспользуйте его только тогда, когда он вам действительно нужен или много времени, вы не поймете, почему ваш патч не работает. (Подумайте, чтобы убрать отрицательный голос из моего ответа, потому что ваши наблюдения неверны)
 05 февр. 2015 г., 21:21
@ Micheled 'Amico Тонкие очки. То, что я предлагаю, - это только потенциальное решение (или, возможно, обходной путь - лучший термин) для некоторых ситуаций, например, когда кто-то выполняет модульное тестирование функции, которая использует isinstance () и которая вызывает метод класса, который может & amp; не смеются из-за этой проблемы. Я думаю, что обходной путь может быть более привлекательным для некоторых, чем ранее предложенные решения. Это сработало для меня в моей ситуации.
 05 февр. 2015 г., 11:12
А если тыneed издеваться над классом, потому что многие внутренние методы не могут использоваться в контексте тестирования? Вопрос, говорящий оInstance and Mocking и неMocking a method а где ответы на Q1 и Q2? Я не понижаю ваш ответ, как вы, но, возможно, вам нужно уделять больше внимания вопросам, прежде чем подавать ответы. О вашем ответе с технической точки зрения зачем использоватьpatch.object вам это действительно не нужно. Ты можешь использовать@patch('__main__.HelloWorld.say_it', return_value='I am fake') это более кратко и проще для чтения.

Ваш ответ на вопрос