如何在一处设置带Mock属性的类补丁,避免重复调用助手函数?
问题描述
需要测试ExampleClass类,该类初始化时会创建SubComponent实例,其example_function_double_value方法依赖SubComponent的value属性:
class ExampleClass: def __init__(self, specific_args): # Do something with specific args self.specific_args = specific_args self.sub_component = SubComponent(...) def example_function_double_value(self) -> float: return SomeFunction(self.specific_args, self.sub_component.value)
当前单元测试通过@mock.patch('SubComponent_Path.SubComponent')替换SubComponent,并通过助手函数set_patch设置其value为3,但每个测试用例都要手动调用该助手函数:
import unittest from unittest import mock def set_patch(patch: mock.Mock): patch_return_value = mock.Mock() patch_return_value.value = 3 patch.return_value = patch_return_value @mock.patch('SubComponent_Path.SubComponent') class ExampleUnitTest(unittest.TestCase): def test_case1(self, patch): set_patch(patch) specific_args1 = # Generate specific arguments for this test case test_class = ExampleClass(specific_args1) # Some assertion where I expect `subcomponent.value to be 3` def test_case2(self, patch): set_patch(patch) specific_args2 = # Generate specific arguments for this test case test_class = ExampleClass(specific_args2) # Some other assertion where I expect `subcomponent.value to be 3`
有没有更优雅的实现方式,不用在每个测试用例开头都调用助手函数?
优雅的实现方式
方法1:用测试类的setUp统一处理
利用unittest的setUp方法,在每个测试用例执行前自动完成mock的初始化,省去重复调用助手函数的步骤:
import unittest from unittest import mock @mock.patch('SubComponent_Path.SubComponent') class ExampleUnitTest(unittest.TestCase): def setUp(self): # 创建带value属性的mock对象 mock_sub = mock.Mock() mock_sub.value = 3 # 给patch设置返回值 self.sub_patch.return_value = mock_sub def test_case1(self, patch): self.sub_patch = patch # 把patch参数绑定到实例属性,供setUp使用 specific_args1 = # 生成当前测试的参数 test_class = ExampleClass(specific_args1) # 执行你的断言逻辑 def test_case2(self, patch): self.sub_patch = patch specific_args2 = # 生成当前测试的参数 test_class = ExampleClass(specific_args2) # 执行你的断言逻辑
方法2:提前配置patch的return_value
这是最简洁的方案,直接在@mock.patch里预先设置好带value属性的mock对象,所有测试用例自动复用这个配置:
import unittest from unittest import mock # 预先创建好符合要求的mock对象 mock_sub_component = mock.Mock() mock_sub_component.value = 3 # 直接把这个mock对象设为patch的返回值 @mock.patch('SubComponent_Path.SubComponent', return_value=mock_sub_component) class ExampleUnitTest(unittest.TestCase): def test_case1(self, patch): specific_args1 = # 生成参数 test_class = ExampleClass(specific_args1) # 断言逻辑 def test_case2(self, patch): specific_args2 = # 生成参数 test_class = ExampleClass(specific_args2) # 断言逻辑
如果后续有个别测试需要修改value,直接在对应测试方法里重新赋值mock_sub_component.value = 新数值即可,完全不影响其他用例。
方法3:自定义装饰器封装逻辑
如果需要更灵活的复用,可以把mock配置逻辑封装成自定义装饰器,替代原生的@mock.patch:
import unittest from unittest import mock def patch_sub_component(): def decorator(test_cls): # 先应用原生patch patched_cls = mock.patch('SubComponent_Path.SubComponent')(test_cls) # 重写setUp方法,加入mock配置 original_setUp = patched_cls.setUp def new_setUp(self, *args, **kwargs): original_setUp(self, *args, **kwargs) mock_sub = mock.Mock() mock_sub.value = 3 self.sub_patch.return_value = mock_sub patched_cls.setUp = new_setUp # 把patch参数绑定到实例属性 for name, method in patched_cls.__dict__.items(): if name.startswith('test_') and callable(method): def wrapped(self, patch, *args, **kwargs): self.sub_patch = patch return method(self, patch, *args, **kwargs) setattr(patched_cls, name, wrapped) return patched_cls return decorator # 使用自定义装饰器 @patch_sub_component() class ExampleUnitTest(unittest.TestCase): def test_case1(self, patch): specific_args1 = # 生成参数 test_class = ExampleClass(specific_args1) # 断言逻辑 def test_case2(self, patch): specific_args2 = # 生成参数 test_class = ExampleClass(specific_args2) # 断言逻辑
内容的提问来源于stack exchange,提问作者seedship
相关产品推荐
相关产品推荐

