unittest中TestCase与Mixin类setUp/tearDown方法继承执行顺序疑问
你提到的测试结果差异其实不是unittest的特殊处理,完全是Python的方法解析顺序(MRO)和super()的工作原理导致的。咱们一步步拆解你的示例:
先明确核心前提
unittest.TestCase的setUp()/tearDown()是普通的类方法,它们的执行完全遵循Python的继承规则——没有额外的钩子逻辑去自动遍历所有父类的同名方法,只有当你通过super()显式传递调用时,才会沿着MRO链执行后续类的方法。
分析TestCase1(测试成功)的执行流程
TestCase1的MRO顺序是:TestCase1 → TestsMixin → TestsBase → unittest.TestCase → object
当unittest调用setUp()时:
- 首先执行
TestsMixin.setUp()(因为它在MRO的最前面) - 该方法里的
super().setUp()会根据当前实例的MRO,找到TestsMixin的下一个类TestsBase,调用它的setUp() TestsBase.setUp()里的super().setUp()找到TestsBase的下一个类unittest.TestCase,调用它的空实现setUp()- 最终,
self.base_value(来自TestsBase)和self.expected_value(来自TestsMixin)都被正确赋值,测试自然通过。
分析TestCase2(测试失败)的执行流程
TestCase2的MRO顺序是:TestCase2 → TestsBase → unittest.TestCase → TestsMixin → object
执行setUp()时:
- 首先执行
TestsBase.setUp()(它在MRO中排在TestsMixin前面) - 这里的
super().setUp()指向的是unittest.TestCase(因为在TestCase2的MRO里,TestsBase的下一个类是unittest.TestCase,而不是TestsMixin),所以只会调用unittest的空setUp() - 整个流程中,
TestsMixin.setUp()从未被触发——没有任何代码调用它,MRO里它的位置靠后,且前面的方法没有通过super()指向它 - 因此
self.expected_value从未被赋值,测试抛出AttributeError。
为什么会有这种差异?
关键在于你的TestsMixin是普通混入类,没有继承unittest.TestCase:
- 它的
setUp()不是unittest专门处理的钩子方法,只有当该方法被MRO优先选中,并且通过super()传递调用时,才会被执行。 - 在TestCase1中,混入类在MRO前面,所以它的
setUp先执行,并且通过super()触发了后续父类的方法;而TestCase2中,继承自TestCase的TestsBase排在前面,它的super()指向的是unittest的基类,不会触发后面混入类的方法。
如何让TestCase2也正常工作?
有两种常见方案:
让混入类继承
unittest.TestCase:
修改TestsMixin的定义:class TestsMixin(unittest.TestCase): def setUp(self): super().setUp() self.expected_value = 1此时TestCase2的MRO会调整为
TestCase2→TestsBase→TestsMixin→unittest.TestCase→object,TestsBase.setUp()的super()会指向TestsMixin,从而触发它的setUp()。显式调用所有父类的
setUp():
在TestCase2中重写setUp(),直接调用两个父类的方法:class TestCase2(TestsBase, TestsMixin): def setUp(self): TestsBase.setUp(self) TestsMixin.setUp(self) # ... 测试方法这种方式不受MRO顺序影响,确保两个父类的
setUp()都被执行。
内容的提问来源于stack exchange,提问作者tamaroth

