如何在Python中Patch整个类、跳过__init__并自定义方法返回值
Mock Python类时跳过
__init__并自定义方法返回值 问题描述
尝试在Python中patch整个类,跳过包含测试时不想执行的API调用的__init__方法,同时为类的方法定义返回值,请问是否有可行的实现方式?
相关代码
Module.py
class MyClass(): def __init__(self): # 测试时不想执行的API调用 print("This shouldn't print in tests") def my_method(self, a, b): return a + b a = MyClass() a.my_method(1,2)
原尝试测试代码(test_class.py)
import unittest as test from unittest.mock import MagicMock, patch from module import MyClass class TestClass(test.TestCase): # @patch("module.MyClass") # 原以为完全不生效 @patch("module.MyClass.__new__") # 仍会调用__init__ def test_my_class(self, mock_class: MagicMock): mock_class.return_value.my_method.return_value = 99 my_class = MyClass() self.assertEqual(my_class.my_method(1,1), 99) test.main()
尝试patch MyClass.__new__后仍会触发__init__,希望得到其他实现方式(刚接触mock和patch,欢迎指出忽略的要点)。
解决方案
方案1:直接patch整个类(最简洁高效)
当patch整个类时,原类会被替换为MagicMock,调用类构造方法时不会执行原__init__代码,同时可以轻松配置方法返回值。
修改后的测试代码:
import unittest as test from unittest.mock import MagicMock, patch from module import MyClass class TestClass(test.TestCase): @patch("module.MyClass") def test_my_class(self, mock_class: MagicMock): # 获取mock类的实例对象(调用MyClass()会返回该实例) mock_instance = mock_class.return_value # 配置my_method的返回值 mock_instance.my_method.return_value = 99 my_class = MyClass() # 验证返回值符合预期 self.assertEqual(my_class.my_method(1,1), 99) # 可选:验证类被实例化过一次 mock_class.assert_called_once() test.main()
原尝试失败原因:
你之前标注@patch("module.MyClass")不生效,是因为没有正确配置mock_class.return_value的方法返回值。按上述方式配置后,原类的__init__不会执行(不会打印测试中不该出现的内容),同时方法返回值会按设定生效。
方案2:单独patch __init__方法(保留原类其他方法逻辑)
如果只想跳过__init__,但保留类中其他方法的原有实现(或部分mock),可以单独patch__init__:
import unittest as test from unittest.mock import patch, MagicMock from module import MyClass class TestClass(test.TestCase): @patch.object(MyClass, '__init__', return_value=None) @patch.object(MyClass, 'my_method') def test_my_class(self, mock_my_method, mock_init): # 配置my_method的返回值 mock_my_method.return_value = 99 my_class = MyClass() self.assertEqual(my_class.my_method(1,1), 99) # 验证__init__被调用但原代码未执行 mock_init.assert_called_once() test.main()
方案3:使用spec参数创建贴近原类的mock
如果需要mock类同时保留原类的方法签名,避免调用不存在的方法,可以用spec参数:
import unittest as test from unittest.mock import patch from module import MyClass class TestClass(test.TestCase): @patch("module.MyClass", spec=MyClass) def test_my_class(self, mock_class): mock_class.return_value.my_method.return_value = 99 my_class = MyClass() self.assertEqual(my_class.my_method(1,1), 99) # 调用原类不存在的方法会抛出AttributeError,符合原类结构 with self.assertRaises(AttributeError): my_class.non_existent_method() test.main()
关键要点提醒
- patch的目标位置:必须patch被测试代码中类的引用位置,而非测试文件中导入的类。比如测试文件中
from module import MyClass,则patch"module.MyClass"才是正确的,因为这是原模块中的类定义位置。 __new__与__init__的关系:__new__负责创建实例,__init__负责初始化实例。即使patch了__new__,如果返回的是类的实例,Python仍会自动调用__init__,这就是你之前patch__new__仍触发__init__的原因。- MagicMock的层级:patch类后,
mock_class是类的mock对象,mock_class.return_value是实例的mock对象,需要给实例的方法配置返回值。
内容的提问来源于stack exchange,提问作者anthony_brother
相关产品推荐
相关产品推荐

