Это то, с чем я боролся долгое время, но, думаю, я наконец нашел решение.
Как вы уже заметили, если вы попытаетесь заменить базовый класс на Mock, класс, который вы пытаетесь протестировать, просто превращается в макет, который побеждает вашу способность его тестировать. Решение состоит в том, чтобы высмеять только методы базового класса, а не весь базовый класс, но это проще сказать, чем сделать: это может быть довольно склонным к ошибкам каждый раз по одному методу тестировать на основе теста.
В результате я создал класс, который сканирует другой класс и присваивает себе Mock()
s, которые соответствуют методам другого класса. Затем вы можете использовать этот класс вместо реального базового класса при тестировании.
Вот фальшивый класс:
class Fake(object):
"""Create Mock()ed methods that match another class's methods."""
@classmethod
def imitate(cls, *others):
for other in others:
for name in other.__dict__:
try:
setattr(cls, name, Mock())
except (TypeError, AttributeError):
pass
return cls
Так, например, вы могли бы иметь некоторый код, как это (Извинения это немного надуманный, просто предположим, что BaseClass
и SecondClass
делают нетривиальную работу и содержать многие методы и даже не обязательно определяется вами на всех):
class BaseClass:
def do_expensive_calculation(self):
return 5 + 5
class SecondClass:
def do_second_calculation(self):
return 2 * 2
class MyClass(BaseClass, SecondClass):
def my_calculation(self):
return self.do_expensive_calculation(), self.do_second_calculation()
Вы бы тогда быть в состоянии написать несколько тестов, как это:
class MyTestCase(unittest.TestCase):
def setUp(self):
MyClass.__bases__ = (Fake.imitate(BaseClass, SecondBase),)
def test_my_methods_only(self):
myclass = MyClass()
self.assertEqual(myclass.my_calculation(), (
myclass.do_expensive_calculation.return_value,
myclass.do_second_calculation.return_value,
))
myclass.do_expensive_calculation.assert_called_once_with()
myclass.do_second_calculation.assert_called_once_with()
Таким образом, методы, существующие на базовых классах, остаются доступными как макеты, с которыми вы можете взаимодействовать, но ваш класс сам по себе не является макетом.
И я был уверен, что это работает как в python2, так и в python3.
Вам действительно нужен объект Mock в качестве базового класса? Будет ли простой «объект» работать? –
Что такое MagicMock? класс или генератор классов? Обычно вы просто 'class RealClass (mock.MagicMock):' – cmd