[英]Python mock: mocking base class for inheritance
我正在測試一個 class,它從另一個非常復雜的繼承而來,具有 DB 連接方法和一堆依賴項。 我想模擬它的基數 class 以便我可以很好地使用子類中定義的方法,但是在我從模擬的 class 繼承的那一刻,object 本身變成了一個模擬並丟失了它的所有方法。
我如何模擬超類?
大致情況可以概括為:
import mock
ClassMock = mock.MagicMock()
class RealClass(ClassMock):
def lol(self):
print 'lol'
real = RealClass()
real.lol() # Does not print lol, but returns another mock
print real # prints <MagicMock id='...'>
這是一個簡化的案例。 實際發生的是RealClass
擴展AnotherClass
,但我設法攔截了AnotherClass
並將其替換為模擬。
這是我一直在努力解決的問題,但我想我終於找到了解決方案。
正如您已經注意到的,如果您嘗試用 Mock 替換基類,則您嘗試測試的類只會變成模擬,這會破壞您測試它的能力。 解決方案是只模擬基類的方法而不是整個基類本身,但這說起來容易做起來難:在逐個測試的基礎上一個一個地模擬每個方法可能很容易出錯。
我所做的是創建一個類來掃描另一個類,並將與另一個類上的方法匹配的Mock()
分配給自己。 然后,您可以在測試中使用此類代替真正的基類。
這是假的類:
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 中都有效。
這應該對你有用。
import mock
ClassMock = mock.MagicMock # <-- Note the removed brackets '()'
class RealClass(ClassMock):
def lol(self):
print 'lol'
real = RealClass()
real.lol() # Does not print lol, but returns another mock
print real # prints <MagicMock id='...'>
你不應該像你那樣傳遞類的實例。 mock.MagicMock
是一個類,所以你直接傳遞它。
In [2]: inspect.isclass(mock.MagicMock)
Out[2]: True
我遇到了類似的問題,並且能夠通過@patch.object
做到這一點。 請參閱官方 python 文檔中補丁裝飾器的示例。
class MyTest(unittest.TestCase):
@patch.object(SomeClass, 'inherited_method')
def test_something(self, mock_method):
SomeClass.static_method()
mock_method.assert_called_with()
只是舉例說明@Akash的回答,這實際上解決了我的 inheritance 模擬挑戰:
@patch.object(SomeClassInheritingAnother, "inherited_method")
def test_should_test_something(self, mocked_inherited_method, mocker, caplog):
#Mocking an HTTP result status code
type(mocked_inherited_method.return_value).status_code = mocker.PropertyMock(return_value=200)
#Calling the inherited method, that should end up using the mocked method
SomeClassInheritingAnother.inherited_method()
#Considering that the request result is being logged as 'Request result: {response.status_code}'
assert "Request result: 200" in caplog.text
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.