[英]Can a python constructor be mocked without mocking other properties of the object?
是否有可能在繼續使用同名的其他字段/函數的生產版本時模擬python構造函數? 例如,給定生產代碼:
class MyClass:
class SubClass:
def __init__(self) -> None:
print("\nreal sub init called")
class SubSubClass:
def __init__(self) -> None:
print("\nreal sub sub init called")
以及以下測試代碼:
class FakeSubClass:
def __init__(self) -> None:
print("\nfake init called")
def test():
MyClass.SubClass()
MyClass.SubClass.SubSubClass()
MyClass.SubClass = Mock(side_effect=FakeSubClass)
MyClass.SubClass()
MyClass.SubClass.SubSubClass()
我們得到以下輸出:
real sub init called
real sub sub init called
fake init called
請注意,最后一行MyClass.SubClass.SubSubClass()
沒有創建真正的SubSubClass,因為此時它是SubClass mock的自動創建的屬性。
我想要的輸出如下:
real sub init called
real sub sub init called
fake init called
real sub sub init called
換句話說,我想只模擬SubClass,而不是SubSubClass。 我嘗試過的東西代替上面的模擬線(兩者都不起作用):
MyClass.SubClass.__init__ = Mock(side_effect=FakeSubClass.__init__)
MyClass.SubClass.__new__ = Mock(side_effect=FakeSubClass.__new__)
請注意,我知道可以通過幾種方法重構代碼以避免此問題,但遺憾的是代碼無法重構。
你也可以偽造這個類, MyClass.SubClass.SubSubClass()
在你的情況下不能工作的是MyClass.SubClass
是一個Mock沒有SubSubClass
定義。 簡單地讓FakeSubClass
從FakeSubClass
繼承它FakeSubClass
將解決問題。
您可以輕松地將MyClass
修補到FakeClass
,您將擁有正確的測試對象而不是真實對象。
from unittest.mock import Mock
class MyClass:
class SubClass:
def __init__(self) -> None:
print("\nreal sub init called")
class SubSubClass:
def __init__(self) -> None:
print("\nreal sub sub init called")
class FakeSubClass(MyClass.SubClass, Mock):
def __init__(self) -> None:
print("\nfake init called")
class FakeClass:
class SubClass(FakeSubClass):
pass
def test():
MyClass.SubClass()
MyClass.SubClass.SubSubClass()
MyClass.SubClass = Mock(side_effect=FakeSubClass)
FakeClass.SubClass()
FakeClass.SubClass.SubSubClass()
我同意ZhouQuan有一個非常好的答案,因為它適用於MyClass.Subclass
上的任何方法或變量。 也就是說,這里有一些可能有用或可能沒用的變化。
如果由於某種原因無法直接編輯FakeSubClass
,或者您只想繼承SubSubClass()
而不是其他內容,則可以在test()
更改它。
def test():
MyClass.SubClass()
MyClass.SubClass.SubSubClass()
FakeSubClass.SubSubClass = MyClass.SubClass.SubSubClass()
MyClass.SubClass = Mock(side_effect=FakeSubClass)
MyClass.SubClass()
MyClass.SubClass.SubSubClass()
我認為可能值得注意的是, Mock
確實接受了一個wraps
參數,它可以用來獲得類似的行為,盡管它並不完全是你要求的。 這是一個例子。
from unittest.mock import Mock
class MyClass:
class SubClass:
def __init__(self) -> None:
print("\nreal sub init called")
class SubSubClass:
def __init__(self) -> None:
print("\nreal sub sub init called")
class FakeSubClass:
def __init__(self) -> None:
print("\nfake init called")
def test():
MyClass.SubClass()
MyClass.SubClass.SubSubClass()
MyClass.SubClass = Mock(side_effect=FakeSubClass, wraps=MyClass.SubClass)
MyClass.SubClass()
MyClass.SubClass.SubSubClass()
這給出了不同的輸出。
real sub init called
real sub sub init called
fake init called # A call to MyClass.SubClass() causes both real and fake inits.
real sub init called # Same MyClass.SubClass() call.
real sub sub init called # But now the SubSubClass() does resolve correctly.
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.