如何在pytest中捕获并校验所有标准输出内容?
首先,你原来的测试代码犯了一个顺序错误——你先调用了capsys.readouterr(),然后才执行产生输出的函数,这时候读取到的其实是调用前的空输出,自然无法校验后续打印的内容。调整顺序就能解决大部分问题,而且capsys本身就可以捕获所有写入sys.stdout的内容,不管是用print还是直接调用sys.stdout.write,完全不需要自己去monkeypatch。
正确使用capsys捕获全量输出
先看修正后的测试代码:
def test_clear(capsys): # 先执行产生输出的函数 outputs_more_than_one_line() # 再读取捕获的所有输出 captured = capsys.readouterr() # 断言完整的输出内容(注意要包含换行符,因为print默认会加\n) assert captured.out == '第一行打印\nlast line printed\n'
这里的关键是:先执行输出逻辑,再读取捕获结果。capsys.readouterr()会返回一个包含out(标准输出)和err(标准错误)的对象,其中out包含了测试函数执行期间所有写入sys.stdout的内容,不管是通过print还是sys.stdout.write()的调用。
比如假设你的outputs_more_than_one_line()是这样的:
import sys def outputs_more_than_one_line(): print('第一行打印') sys.stdout.write('last line printed\n')
那上面的测试就能正确匹配到所有输出。
分阶段捕获输出(可选)
如果你需要分阶段验证输出(比如先执行一部分逻辑,校验输出,再执行另一部分),可以多次调用readouterr()——每次调用后,缓冲区会被清空,下一次读取的是后续产生的新输出:
def test_staged_output(capsys): print('第一阶段输出') captured1 = capsys.readouterr() assert captured1.out == '第一阶段输出\n' sys.stdout.write('第二阶段输出\n') captured2 = capsys.readouterr() assert captured2.out == '第二阶段输出\n'
为什么capsys比monkeypatch print更可靠
你提到的monkeypatchbuiltins.print的方法确实有局限性:因为print只是Python提供的高层封装,有些代码会直接操作底层的sys.stdout(比如调用sys.stdout.write、sys.stdout.flush),这时候monkeypatchprint就完全捕获不到这些输出。而capsys是pytest专门设计的输出捕获工具,它会直接拦截整个sys.stdout和sys.stderr的流,不管上层用什么方式写入,都能被捕获到,健壮性高得多。
内容的提问来源于stack exchange,提问作者Mattwmaster58

