pytest需通过conftest.py中自定义pytest_terminal_summary hook修改terminalsummary输出,利用terminalreporter.write_line()或section()追加内容,避免print()、异常及编码问题,并手动注入和统计字段。

pytest如何通过hook函数修改terminalsummary输出
pytest本身不提供直接配置项来增减测试总结行,必须通过自定义pytest_terminal_summary hook实现。这个hook在测试运行结束后被调用,接收terminalreporter对象,你可以用它往终端输出里追加任意文本。
- hook必须放在
conftest.py中(项目根目录或测试目录下),不能写在测试文件里 -
terminalreporter有write_line()和section()两个常用方法:前者纯文本换行,后者带分隔线更醒目 - 不要在hook里调用
print()——会破坏pytest的输出时序和颜色控制 - 如果想显示统计值(如跳过数、耗时),需从
terminalreporter.stats或config中提取,不能硬编码
获取并显示自定义统计字段(比如环境/版本/用例覆盖率)
terminalsummary本身不包含业务上下文,所有“自定义字段”都得你自己计算或注入。常见做法是:提前把数据存进config或session对象,在hook里读取。
- 推荐在
pytest_configure(config)中存入基础信息:config._metadata['ENV'] = 'staging' - 若需动态统计(如“标记为
@smoke的用例执行数”),可在pytest_runtest_makereport中累计到session属性上 -
terminalreporter.stats只含pytest原生状态('passed'/'failed'/'skipped'等),不包含自定义标记统计,必须自己维护 - 注意:metadata默认会被
pytest-html插件读取,但terminalsummary里不会自动展示,仍需手动输出
避免覆盖默认summary或引发UnicodeEncodeError
直接调用terminalreporter.write_line()一般没问题,但在CI环境(如GitLab CI的alpine镜像)或Windows cmd下容易因编码问题崩溃,错误信息通常是UnicodeEncodeError: 'charmap' codec can't encode character。
- 始终用
str()包裹待输出内容,避免传入None或未解码bytes - 对可能含中文/emoji的字段,先做安全encode:
s.encode('utf-8', errors='ignore').decode('utf-8') - 不要在
write_line()里拼接长字符串+换行符,write_line()已自带换行;重复调用即可分多行 - 避免在hook里raise异常——会导致pytest退出码变成2,掩盖真实测试失败
一个可直接复用的conftest.py片段
下面这段代码添加了环境名、Python版本、以及统计跳过用例中带reason="not implemented"的数量:
立即学习“Python免费学习笔记(深入)”;
def pytest_configure(config):
config._metadata['Python'] = config._metadata.get('Python', f"{sys.version_info.major}.{sys.version_info.minor}")
def pytest_terminal_summary(terminalreporter, exitstatus, config):
tr = terminalreporter
tr.section("CUSTOM SUMMARY", sep="-", blue=True)
tr.write_line(f"Environment: {config._metadata.get('ENV', 'unknown')}")
tr.write_line(f"Python: {config._metadata['Python']}")
skipped = terminalreporter.stats.get('skipped', [])
not_impl_count = sum(1 for rep in skipped if hasattr(rep, 'longrepr') and 'not implemented' in str(rep.longrepr))
tr.write_line(f"NOT_IMPLEMENTED skipped: {not_impl_count}")
真正难的不是写这几行,而是决定哪些字段值得放进summary——太多会淹没关键失败信息,太少又失去定制意义。每次加新字段前,先问一句:这个值是否会影响我立刻判断CI是否要人工介入?


















