
本文介绍如何使用 pytest 捕获并断言 python 函数的标准输出(stdout),从而自动化验证学生编写的 `print()` 语句是否完全符合预期,适用于编程教学场景中的即时反馈与自动评测。
在编程教学中,精准验证学生是否写出完全正确的可执行代码(而非仅检查语法或函数定义)至关重要。例如,你期望学生实现一个函数 print1(x),其行为是调用 print("Hello world") 并输出该字符串——但注意:print() 本身返回 None,且不直接返回字符串,因此不能通过 assert print1 is "Hello world" 或 assert print1() == "Hello world" 来验证(后者还会因 print() 返回 None 而失败)。
正确做法是:捕获函数执行时实际写入标准输出(stdout)的内容,并与期望字符串比对。Python 标准库提供了轻量、安全的工具来实现这一点:contextlib.redirect_stdout 配合 io.StringIO。
以下是完整、可运行的测试示例:
import io
import contextlib
def test_print1():
# 创建一个 StringIO 对象用于捕获输出
with contextlib.redirect_stdout(io.StringIO()) as captured:
print1() # 执行学生函数
# 断言捕获到的输出内容(注意:print() 默认换行,所以实际输出是 "Hello world\n")
assert captured.getvalue() == "Hello world\n"⚠️ 关键注意事项:
- print() 默认末尾添加换行符 \n,因此期望值必须包含 \n;若需忽略换行差异,可用 captured.getvalue().strip() == "Hello world";
- 不要尝试修改 sys.stdout 全局对象——redirect_stdout 是上下文管理器,能确保退出时自动恢复,避免测试间污染;
- 此方法仅验证输出行为,不校验学生是否“写了 print("Hello world") 这行字面代码”(那是静态分析范畴);但对于教学目标——“程序运行后正确显示指定文本”——已完全足够且更健壮;
- 若函数需接收参数(如 print1(x) 中的 x),请确保测试中传入合理值(例如 print1("unused")),或重构函数使其逻辑清晰(当前示例中 x 未被使用,建议明确用途或移除)。
一旦此测试通过,即可依样扩展至其他练习:为每个教学模块编写独立测试函数,覆盖 print、简单计算、列表操作等常见任务。配合 pytest 的 -v 参数运行,还能为学生提供清晰的失败提示(如 “Expected 'Hello world\n', got 'hello world\n'”),大幅提升自学反馈效率。
这套轻量方案无需额外依赖,兼容 Python 3.4+,完美替代 CodeKeyz 的核心评测能力——你不仅保住了高效的教学模式,更获得了完全可控、可定制、可持续维护的自动化评测体系。










