pytest --last-failed 漏跑仍失败的用例:自定义名称里的方括号
自定义收集器生成含方括号的 Item 名称时,失败缓存还原出的 NodeId 可能与实时对象不相等。另一个失败项修好后,--last-failed 可能只跑它并报告成功。
自定义收集器生成的用例仍会失败,第二次加上 --last-failed 却只跑了另一个已经修好的用例,最终返回成功。如果被漏掉的 Item 名称含有字面方括号,例如 a_bad[one],应检查失败缓存与实时用例的身份是否一致。
明确出现过这种情况的是 pytest 9.2.0.dev335。正式 9.2.0 的失败缓存读取与 NodeId 解析仍保留这里的结构差异:名称中的括号可能被还原成参数字段,不能仅凭两个 nodeid 打印相同就认为筛选一定正确。
为什么还需要另一个失败项
设自定义收集器第一次生成两个失败项:a_bad[one] 和 b_fixed。下一次只修好 b_fixed,前一个仍失败。完整运行应继续失败;受影响的 --last-failed 运行却可能只选择 b_fixed,然后显示一个通过项。
实时对象可把整个 a_bad[one] 保存为名称,参数字段为空。将它写成字符串再解析时,NodeId 则可能把名称还原为 a_bad,把 one 放入参数字段。字符串表示相同,参与相等和哈希判断的结构字段却不同,缓存筛选便可能漏掉实时 Item。
只有一个含括号的失败项时,“找不到匹配的历史失败”的回退可能运行全部用例,反而让问题不显眼。验收只包含一个失败项是不够的。这里也不等同于普通参数化用例都受影响;重点是自定义收集器把括号放在名称本身的构造方式。
将完整运行作为放行条件
临时处理应保留自定义输入和 Item 名称,直接完整运行对应的自定义测试文件。假设收集器已支持项目中的 test_cases.json,且命令行和项目配置没有另外启用失败筛选:
python -m pytest -v test_cases.json
项目若在 addopts 或环境参数中默认加入 --lf,需要按原配置机制取消那项筛选,否则看似完整的命令仍可能被过滤。核对详细收集清单,确认两个名称都实际运行,并让仍失败的项继续阻止放行。
不要把清空缓存后的一次全跑当成身份问题已修复。缓存重新积累后仍可能回到同样条件。把括号从名称中删掉会改变用例标识、历史结果和外部选择规则;若要调整收集器,应先明确这些兼容影响,而不是为了让一次 CI 变绿临时改名。
验收包含两次失败与一次修复
保留同一份自定义数据,依次核对首次两个失败、只修好普通名称项后的失败重跑,以及同输入的完整运行。第二步应仍包含含括号且未修复的项,并保持失败退出。还要比较完整 nodeid 与实际运行数量,不能只检查退出码。
包含修正的依赖或收集器变更采用后,也应沿用这组场景。插件的自定义 Item 和普通函数参数化用例应分别覆盖,避免用一类通过结果替代另一类的身份验证。


