{
  "id": "python/pytest-subtests-failure-not-counted",
  "signature": "FAILED tests/test_x.py::TestX::test_y - subtest failed\n# but exit code is 0 and CI passes",
  "signature_zh": "FAILED tests/test_x.py::TestX::test_y - 子测试失败\n# 但退出码为 0 且 CI 通过",
  "regex": "FAILED\\ tests/test_x\\.py::TestX::test_y\\ \\-\\ subtest\\ failed\\\n\\#\\ but\\ exit\\ code\\ is\\ 0\\ and\\ CI\\ passes",
  "domain": "python",
  "category": "runtime_error",
  "subcategory": null,
  "root_cause": "unittest subTest failures are reported but pytest's exit code may remain 0 if the test method itself doesn't propagate the failure, especially in older pytest or with plugins that swallow subtest outcomes.",
  "root_cause_type": "generic",
  "root_cause_zh": "unittest subTest 失败会被报告，但如果测试方法本身未传播失败，pytest 的退出码可能仍为 0，尤其在较旧 pytest 或吞掉子测试结果的插件中。",
  "versions": [
    {
      "version": "7.x",
      "introduced": null,
      "deprecated": null,
      "removed": null,
      "behavior_change": null,
      "status": "active"
    },
    {
      "version": "8.x",
      "introduced": null,
      "deprecated": null,
      "removed": null,
      "behavior_change": null,
      "status": "active"
    }
  ],
  "os_specific": {},
  "dead_ends": [
    {
      "action": "",
      "why_fails": "Stops on first failure but doesn't change exit code semantics for subtest failures.",
      "fail_rate": 0.85,
      "condition": "",
      "sources": []
    },
    {
      "action": "",
      "why_fails": "Changes traceback style, not exit code behavior.",
      "fail_rate": 0.95,
      "condition": "",
      "sources": []
    },
    {
      "action": "",
      "why_fails": "Loses the per-subtest granularity that subTest was meant to provide.",
      "fail_rate": 0.7,
      "condition": "",
      "sources": []
    }
  ],
  "workarounds": [
    {
      "action": "",
      "success_rate": 0.95,
      "how": "pip install -U pytest\n# pytest >= 7.2 correctly reports subtest failures in exit code",
      "condition": "",
      "sources": []
    },
    {
      "action": "",
      "success_rate": 0.85,
      "how": "def test_y(self):\n    errors = []\n    for case in cases:\n        with self.subTest(case=case):\n            try:\n                check(case)\n            except AssertionError as e:\n                errors.append(e)\n    if errors:\n        raise AssertionError(errors)",
      "condition": "",
      "sources": []
    }
  ],
  "workarounds_zh": [],
  "transition_graph": {
    "leads_to": [],
    "preceded_by": [],
    "frequently_confused_with": []
  },
  "official_doc_url": null,
  "official_doc_section": null,
  "error_code": null,
  "verification_tier": "ai_generated",
  "confidence": 0.82,
  "fix_success_rate": 0.8,
  "resolvable": "true",
  "first_seen": "2025-05-20",
  "last_confirmed": "2025-01-01",
  "last_updated": "2025-01-01",
  "evidence_count": 0,
  "tags": [],
  "locale": "en",
  "aliases": []
}