ERROR: test_foo (tests.test_x.TestCase.test_foo) ---------------------------------------------------------------------- Traceback (most recent call last): File "...", 第 N 行, 在 tearDown 中 self.conn.close() AttributeError: 'NoneType' 对象没有属性 'close'
ERROR: test_foo (tests.test_x.TestCase.test_foo) ---------------------------------------------------------------------- Traceback (most recent call last): File "...", line N, in tearDown self.conn.close() AttributeError: 'NoneType' object has no attribute 'close'
ID: python/unittest-teardown-exception-masked
版本兼容性
| 版本 | 状态 | 引入 | 弃用 | 备注 |
|---|---|---|---|---|
| 3.10 | active | — | — | — |
| 3.11 | active | — | — | — |
| 3.12 | active | — | — | — |
根因分析
即使 setUp 失败,tearDown 也会运行,因此资源为 None;teardown 异常替换了原始测试错误,掩盖了真正的失败。
English
tearDown runs even when setUp failed, so resources are None; the teardown exception replaces the original test error, obscuring the real failure.
解决方案
-
95% 成功率
def setUp(self): self.conn = create_conn() self.addCleanup(self.conn.close) # If create_conn raises, addCleanup is never registered, so tearDown does nothing -
85% 成功率
def tearDown(self): conn = getattr(self, 'conn', None) if conn is not None: conn.close()
无效尝试
常见但无效的做法:
-
90% 失败
Silences real teardown errors and leaks resources, causing cascading failures in later tests.
-
85% 失败
Resources (DB connections, temp files) leak between tests, causing flakiness and cross-test pollution.
-
60% 失败
Symptom-level fix; the underlying setUp failure still hides behind the teardown error path.