在继续使用生产版本的其他同名字段/函数时,是否可以模拟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模拟的自动创建的属性。
我想要的输出如下:
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__)
请注意,我知道可以通过几种方式来重构代码,以避免出现此问题,但遗憾的是,无法重构代码。
答案 0 :(得分:2)
您还可以伪造该类,MyClass.SubClass.SubSubClass()
在您的情况下不起作用,是MyClass.SubClass
是一个没有SubSubClass
定义的Mock。只需让FakeSubClass
从MyClass继承它,SubClass就可以解决问题。
您可以轻松地将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()
答案 1 :(得分:1)
我同意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
参数,尽管它并不是您所要的,但它可以用来获得 like 行为。对于。这是一个例子。
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.