单元测试替换类基类触发TypeError,求排查与解决方法
问题分析与解决方案
错误原因
你直接将原Bar类的__dict__复制到新创建的类中,导致__init__方法里的super()仍绑定着原Bar类的上下文。Python 3中无参数的super()会隐式使用方法定义时的类(即继承自Foo的原Bar),当新Bar实例调用该方法时,super()等价于super(原Bar, self),但新Bar实例并非原Bar的子类或实例,因此触发类型错误。
解决方案
方案一:临时修改原类的基类(最简单)
不需要创建新类,直接临时替换Bar的__bases__属性,测试完成后恢复:
class Foo: pass class Bar(Foo): def __init__(self): super().__init__() import pytest @pytest.fixture def uut(): global Bar old_bases = Bar.__bases__ # 临时将Bar的基类改为object Bar.__bases__ = (object,) try: yield Bar finally: # 测试结束后恢复原基类 Bar.__bases__ = old_bases def test_Bar(uut): b = uut()
这种方式会让Bar的__init__方法中的super()动态指向当前基类object,调用super().__init__()不会有任何问题。
方案二:创建新类时重新定义__init__
如果必须创建新类,需要为新类重新定义__init__,避免复用原方法的绑定上下文:
class Foo: pass class Bar(Foo): def __init__(self): super().__init__() import types import pytest @pytest.fixture def uut(): global Bar old_bar = Bar # 为新类定义独立的__init__方法 def new_init(self): super().__init__() # 复制原Bar的其他属性,但排除原__init__ class_attrs = {k: v for k, v in Bar.__dict__.items() if k != '__init__'} new_bar = types.new_class(Bar.__name__, (object,), {}, lambda ns: ns.update(class_attrs)) new_bar.__init__ = new_init try: yield new_bar finally: Bar = old_bar def test_Bar(uut): b = uut()
新的__init__方法是为新类定义的,super()会正确指向新类的基类object。
补充说明
如果你尝试过用mock.Mock作为基类,同样会遇到相同问题——只要复用原Bar的__init__方法,super()就会绑定原类上下文。上述两种方案都能解决这个核心问题。
内容的提问来源于stack exchange,提问作者Tom
相关产品推荐
相关产品推荐

