pytest的断言指南说,普通Python assert失败时,工具可以显示常见子表达式的值,包括函数调用、属性、比较,以及二元和一元运算。指南开头的纸面例子中,预期的四与函数返回的三一起进入失败说明,返回值又被联系到产生它的调用。这份额外信息解释失败发生在哪一层表达式,不是把一条失败结论换成更醒目的颜色。[1]
自行附带的断言消息也没有因此被替换掉。文档说明,消息会与断言自省信息一同出现在回溯中。一个来源是写在断言旁边的说明文字,另一个来源是工具对表达式及其值的呈现。两类文字可以同时出现,所以报告里有了人为写好的消息,并不意味着子表达式信息必然被它覆盖;反过来,看到返回值细节,也不说明消息来自那份返回值。[1]
比较对象不同,补充信息的形式也不同。长字符串可以显示上下文差异,长序列可指出最先失败的索引,字典则列出不同条目。对于字符串差异,原文还明确减号对应比较左侧,加号对应右侧。索引位置、键值条目与两侧文字各自回应所比较的对象;不能仅凭报告里都出现了差异说明,就把一个序列索引当成字典的键,或把左右两侧反写。[1]
更具体的说明可以来自pytest_assertrepr_compare。指南对这个hook的定义是,为失败的比较表达式返回解释;参数包括配置对象、运算符、左操作数和右操作数。返回None表示没有自定义解释,否则返回字符串列表。列表各项将以换行连接,但单项字符串内部的换行会被转义;第一行作为摘要,其余行略作缩进。因此,列表分成多少项与某一项内部带了多少换行,并不是相同的排版安排。[1]
这种解释还有按测试项确定的来源范围。对于给定item,pytest只询问该item所在目录及其祖先目录中的conftest定义,并不是任意目录下的同名定义都会参与。解释内容由hook返回什么决定,哪些定义能为这一测试项提供解释,又由文档中的目录关系限定。这里说明的是资料来源的适用范围,没有把某个具体项目已经加载了哪些定义当作既成事实。[1]
失败报告能带上上述自省信息,指南将其联系到运行前的assert重写。自动重写只覆盖测试收集过程直接发现的测试模块;辅助模块如果本身不是测试模块,其中的断言不会自动获得同样处理。页面另把手动登记与导入先后联系起来,要求登记发生在导入之前。由此,文字上都叫一个assert,并不足以省略它所在模块是否属于直接收集对象这项条件。[1]
重写后的模块可以再写回磁盘作为pyc缓存,但缓存不是自省信息唯一成立的条件。原文明确,即使不写这些pyc文件,断言自省的好处仍然存在,变化只是磁盘不再缓存它们;在只读文件系统或zip文件等不能写入的情况下,重写过程也会跳过缓存。这里消失的是可供后续使用的磁盘文件,并非由此认定本次失败解释已经退回普通断言文字。[1]
指南最后还把测试函数的返回值单独提醒:返回非None会产生PytestReturnNotNoneWarning,返回True或False不会因此决定测试通过或失败。这个值属于函数返回,而非前面hook所规定的解释列表。虽然两处都谈到返回值,前者面对测试函数,后者面对失败比较解释的提供者,不能把hook的None或字符串列表规则搬到测试结果上。[1]
本文限当前stable指南的文字说明,未运行测试、实现hook或更改缓存与重写设置。示例中的pytest版本占位和页尾版权不作为精确发行版或首次日期。
信息来源
本文基于上述公开资料整理,未使用来源页面的图片、视频或嵌入媒体。