Я использую фреймворк python для тестирования (http://www.voidspace.org.uk/python/mock/), и я хочу издеваться над суперклассом и сосредоточиться на тестировании добавленного поведения подклассов.
(Для тех, кого это касается, у меня есть расширенный pymongo.collection.Collection, и я хочу только проверить свое добавленное поведение. Я не хочу запускать mongodb в качестве другого процесса для целей тестирования.)
Для этого обсуждения A является суперклассом, а B - подклассом. Кроме того, я определяю прямые и косвенные вызовы суперкласса, как показано ниже:
class A(object):
def method(self):
...
def another_method(self):
...
class B(A):
def direct_superclass_call(self):
...
A.method(self)
def indirect_superclass_call(self):
...
super(A, self).another_method()
Подход № 1
Определите класс mock для A называется MockA и используйте mock.patch, чтобы заменить его для теста во время выполнения. Это обрабатывает прямые вызовы суперкласса. Затем манипулируйте B.__ base__ для обработки непрямых вызовов суперкласса. (см. ниже)
Возникает проблема в том, что мне приходится писать MockA, а в некоторых случаях (как и в случае pymongo.collection.Collection) это может потребовать много работы, чтобы разгадать все внутренние вызовы, чтобы издеваться.
Подход № 2
Требуемый подход состоит в том, чтобы каким-то образом использовать класс mock.Mock() для обработки вызовов в макете как раз вовремя, так и с определенным значением return_value или side_effect на месте теста. Таким образом, я должен делать меньше работы, избегая определения MockA.
Проблема, с которой я сталкиваюсь, заключается в том, что я не могу понять, как изменить B.__ основания__, чтобы экземпляр mock.Mock() можно было внедрить в качестве суперкласса (мне нужно каким-то образом сделать прямое связывание здесь). До сих пор я определил, что super() проверяет MRO, а затем вызывает первый класс, который определяет данный метод. Я не могу понять, как получить суперкласс для обработки чека и добиться успеха, если он встречается с макетным классом. __getattr__, похоже, не используется в этом случае. Я хочу, чтобы супер подумал, что метод определен в этой точке, а затем использовать функцию mock.Mock(), как обычно.
Как супер() обнаруживает, какие атрибуты определены в классе в последовательности MRO? И есть ли способ для меня вмешаться сюда и каким-то образом заставить его использовать mock.Mock() на лету?
import mock
class A(object):
def __init__(self, value):
self.value = value
def get_value_direct(self):
return self.value
def get_value_indirect(self):
return self.value
class B(A):
def __init__(self, value):
A.__init__(self, value)
def get_value_direct(self):
return A.get_value_direct(self)
def get_value_indirect(self):
return super(B, self).get_value_indirect()
# approach 1 - use a defined MockA
class MockA(object):
def __init__(self, value):
pass
def get_value_direct(self):
return 0
def get_value_indirect(self):
return 0
B.__bases__ = (MockA, ) # - mock superclass
with mock.patch('__main__.A', MockA):
b2 = B(7)
print '\nApproach 1'
print 'expected result = 0'
print 'direct =', b2.get_value_direct()
print 'indirect =', b2.get_value_indirect()
B.__bases__ = (A, ) # - original superclass
# approach 2 - use mock module to mock out superclass
# what does XXX need to be below to use mock.Mock()?
#B.__bases__ = (XXX, )
with mock.patch('__main__.A') as mymock:
b3 = B(7)
mymock.get_value_direct.return_value = 0
mymock.get_value_indirect.return_value = 0
print '\nApproach 2'
print 'expected result = 0'
print 'direct =', b3.get_value_direct()
print 'indirect =', b3.get_value_indirect() # FAILS HERE as the old superclass is called
#B.__bases__ = (A, ) # - original superclass