Python unittest继承中构造与测试方法多次调用问题排查修复
问题原因分析
- unittest的测试发现机制:所有继承自
unittest.TestCase的类都会被unittest识别为独立测试类,包括你的TestBase基类。当运行TestChild时,unittest会同时执行TestBase和TestChild两个测试类的测试方法。 - 子类继承测试方法:
TestChild继承了TestBase的test_canary_base方法,因此TestChild的实例会额外执行一次该基类测试方法,导致test_canary_base被执行两次(一次是TestBase自身实例,一次是TestChild实例)。 - 测试方法与实例化的绑定:unittest中每个测试方法运行时都会创建一个测试类的实例,因此构造方法的调用次数等于所有被执行的测试方法总数。比如
TestBase有1个测试方法,TestChild有1个继承的+1个自身的,总共3个测试方法,对应TestBase构造方法被调用3次,TestChild构造方法被调用2次。
修复方案与可运行示例
根据需求,提供两种常用修复方式:
方案1:基类作为公共逻辑容器(不执行测试)
将TestBase改为普通类(不继承unittest.TestCase),仅提供初始化、辅助逻辑,让实际测试子类同时继承unittest.TestCase和TestBase。
import unittest # 普通基类,仅提供公共逻辑,不继承TestCase class TestBase: def __init__(self): print("TestBase __init__ called") # 这里放所有测试类共享的初始化逻辑 # 公共辅助方法,不要用test_开头 def _verify_common_condition(self): print("Running base-level verification") return True # 实际测试类,同时继承TestCase和TestBase class TestChild(unittest.TestCase, TestBase): def __init__(self, *args, **kwargs): # 先初始化TestCase,再初始化TestBase super().__init__(*args, **kwargs) TestBase.__init__(self) print("TestChild __init__ called") def test_canary_child(self): print("Running test_canary_child") self.assertTrue(self._verify_common_condition()) self.assertTrue(True) class TestGrandchild(TestChild): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) print("TestGrandchild __init__ called") def test_canary_grandchild(self): print("Running test_canary_grandchild") self.assertTrue(self._verify_common_condition()) self.assertTrue(True) if __name__ == "__main__": unittest.main()
运行结果:
TestBase __init__ called TestChild __init__ called Running test_canary_child Running base-level verification .TestBase __init__ called TestChild __init__ called TestGrandchild __init__ called Running test_canary_grandchild Running base-level verification . ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK
方案2:基类保留测试方法但不单独执行
给TestBase添加@unittest.skip装饰器,阻止其被单独执行,子类可按需继承基类测试方法。
import unittest # 标记基类为跳过,不单独执行 @unittest.skip("Base class for inheritance only, skip direct execution") class TestBase(unittest.TestCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) print("TestBase __init__ called") def test_canary_base(self): print("Running test_canary_base") self.assertTrue(True) class TestChild(TestBase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) print("TestChild __init__ called") def test_canary_child(self): print("Running test_canary_child") self.assertTrue(True) if __name__ == "__main__": unittest.main()
运行结果:
TestBase __init__ called TestChild __init__ called Running test_canary_base .TestBase __init__ called TestChild __init__ called Running test_canary_child . ---------------------------------------------------------------------- Ran 2 tests in 0.000s OK (skipped=1)
如果不需要子类执行基类测试方法,直接删除TestBase中的test_canary_base即可,此时运行TestChild只会执行自身的测试方法,构造方法各调用1次。
内容的提问来源于stack exchange,提问作者user10664542
相关产品推荐
相关产品推荐

