class 属性默认值不接受模拟
class attribute default value doesn't receive the mock
我正在对我编写的模块进行单元测试,遇到向具有模拟对象的函数提供默认 class 对象的问题。
这是它在高级中的样子:
main_file.py
class MainClass(object):
def main_func(self):
sub_class_obj = SubClass()
sub_class_obj.sub_func()
sub_file.py
class SubClass(object):
def sub_func(self, my_att=Helper(2)):
self.my_att = my_att
helpers.py
class Helper():
def __init__(self, my_val):
self.my_val = my_val
test.py
class TestClass(object):
@patch('sub_file.Helper', MockHelper)
def my_test(self):
main_class_obj = MainClass()
main_class_obj.main_func()
当我以提供 my_att 的方式执行此操作时 - 一切正常并且调用了 Mock,但是当我不这样做并且设置了默认值时 - 我得到了原始的 Helper class对象。
知道如何使该属性的默认值也接收模拟吗?
提前致谢!
问题是默认值是在导入时读取的,所以在你修补 Helper
之前它已经在函数中设置好了。此时默认值保存在函数对象中。
但是,您也可以修补函数的默认参数(可以通过 __defaults__
:
from sub_file import SubClass
class TestClass(object):
@patch('sub_file.Helper', MockHelper)
@patch.object(SubClass.sub_func, "__defaults__", (MockHelper(),))
def my_test(self):
main_class_obj = MainClass()
main_class_obj.main_func()
请注意 __defaults__
必须是参数元组。
您也可以使用 monkeypatch
来做同样的事情:
from sub_file import SubClass
class TestClass(object):
@patch('sub_file.Helper', MockHelper)
def my_test(self, monkeypatch):
monkeypatch.setattr(SubClass.sub_func, "__defaults__", (MockHelper(),)
main_class_obj = MainClass()
main_class_obj.main_func()
更新:
我没有意识到这不适用于 Python 2。除了默认参数的其他名称(func_defaults
而不是 __defaults__
)这仅适用于独立函数,但是不使用方法,如 setattr
is not supported 在这种情况下 Python 2。这是 Python 2 的解决方法:
from sub_file import SubClass
class TestClass(object):
@patch('sub_file.Helper', MockHelper)
def my_test(self):
orig_sub_func = SubClass.sub_func
with patch.object(SubClass, "sub_func",
lambda o, attr=MockHelper(): orig_sub_func(o, attr)):
main_class_obj = MainClass()
main_class_obj.main_func()
这样,原来的 sub_func
被替换为具有自己的默认值的函数,但在其他方面将功能委托给原始函数。
更新 2:
刚刚看到@chepner 的回答,这是正确的:最好的方法是相应地重构代码。只有当你做不到时,你才尝试这个答案。
默认值是在定义函数时创建的,而不是在调用函数时创建的。在你的测试中修补 Helper
已经太晚了,因为 SubClass.__init__
已经被定义了。
不过,与其修补任何东西,不如重写 MainClass
,这样就没有对 SubClass
的硬编码引用:这样您就可以自己创建适当的实例,而无需依赖默认值.
可以直接传一个实例:
class MainClass(object):
def main_func(self, sub_class_obj):
sub_class_obj.sub_func()
class TestClass(object):
def my_test(self):
mock_obj = MockHelper()
main_class_obj = MainClass(mock_obj)
main_class_obj.main_func()
或者采用将被调用以创建子类对象的工厂函数。
class MainClass(object):
def main_func(self, factory=SubClass):
sub_class_obj = factory()
sub_class_obj.sub_func()
class TestClass(object):
def my_test(self):
main_class_obj = MainClass(lambda: SubClass(MockHelper()))
main_class_obj.main_func()
我正在对我编写的模块进行单元测试,遇到向具有模拟对象的函数提供默认 class 对象的问题。 这是它在高级中的样子:
main_file.py
class MainClass(object):
def main_func(self):
sub_class_obj = SubClass()
sub_class_obj.sub_func()
sub_file.py
class SubClass(object):
def sub_func(self, my_att=Helper(2)):
self.my_att = my_att
helpers.py
class Helper():
def __init__(self, my_val):
self.my_val = my_val
test.py
class TestClass(object):
@patch('sub_file.Helper', MockHelper)
def my_test(self):
main_class_obj = MainClass()
main_class_obj.main_func()
当我以提供 my_att 的方式执行此操作时 - 一切正常并且调用了 Mock,但是当我不这样做并且设置了默认值时 - 我得到了原始的 Helper class对象。
知道如何使该属性的默认值也接收模拟吗?
提前致谢!
问题是默认值是在导入时读取的,所以在你修补 Helper
之前它已经在函数中设置好了。此时默认值保存在函数对象中。
但是,您也可以修补函数的默认参数(可以通过 __defaults__
:
from sub_file import SubClass
class TestClass(object):
@patch('sub_file.Helper', MockHelper)
@patch.object(SubClass.sub_func, "__defaults__", (MockHelper(),))
def my_test(self):
main_class_obj = MainClass()
main_class_obj.main_func()
请注意 __defaults__
必须是参数元组。
您也可以使用 monkeypatch
来做同样的事情:
from sub_file import SubClass
class TestClass(object):
@patch('sub_file.Helper', MockHelper)
def my_test(self, monkeypatch):
monkeypatch.setattr(SubClass.sub_func, "__defaults__", (MockHelper(),)
main_class_obj = MainClass()
main_class_obj.main_func()
更新:
我没有意识到这不适用于 Python 2。除了默认参数的其他名称(func_defaults
而不是 __defaults__
)这仅适用于独立函数,但是不使用方法,如 setattr
is not supported 在这种情况下 Python 2。这是 Python 2 的解决方法:
from sub_file import SubClass
class TestClass(object):
@patch('sub_file.Helper', MockHelper)
def my_test(self):
orig_sub_func = SubClass.sub_func
with patch.object(SubClass, "sub_func",
lambda o, attr=MockHelper(): orig_sub_func(o, attr)):
main_class_obj = MainClass()
main_class_obj.main_func()
这样,原来的 sub_func
被替换为具有自己的默认值的函数,但在其他方面将功能委托给原始函数。
更新 2:
刚刚看到@chepner 的回答,这是正确的:最好的方法是相应地重构代码。只有当你做不到时,你才尝试这个答案。
默认值是在定义函数时创建的,而不是在调用函数时创建的。在你的测试中修补 Helper
已经太晚了,因为 SubClass.__init__
已经被定义了。
不过,与其修补任何东西,不如重写 MainClass
,这样就没有对 SubClass
的硬编码引用:这样您就可以自己创建适当的实例,而无需依赖默认值.
可以直接传一个实例:
class MainClass(object):
def main_func(self, sub_class_obj):
sub_class_obj.sub_func()
class TestClass(object):
def my_test(self):
mock_obj = MockHelper()
main_class_obj = MainClass(mock_obj)
main_class_obj.main_func()
或者采用将被调用以创建子类对象的工厂函数。
class MainClass(object):
def main_func(self, factory=SubClass):
sub_class_obj = factory()
sub_class_obj.sub_func()
class TestClass(object):
def my_test(self):
main_class_obj = MainClass(lambda: SubClass(MockHelper()))
main_class_obj.main_func()