🧪 Python 文档测试(doctest)超实用入门
你有没有碰到过这种“名场面”:
- 辛辛苦苦写好的接口文档,示例代码拷贝下来居然报错;
- 改完代码忘了更新文档里的例子,用户照着敲出一堆异常;
- 为了验证示例又写了一套单元测试,结果两边重复维护,身心俱疲。
别急,Python 自带一个「隐藏王牌」——doctest 模块,它把可执行的测试用例直接嵌进文档字符串(docstring)里,看起来像一段真实的 Python 交互式对话。既能当文档看,又能一键跑测试, 真正的「写一次,用两遍」!
为什么选择 doctest?
doctest 不是万能的测试框架,但在“轻量、可读、零依赖”这个赛道上,几乎没有对手:
-
✅ 自文档化代码
最好的教程,就是可以运行并验证的代码。doctest 让你的示例本身就是测试用例,不需要单独维护文档和测试。 -
🔄 强制文档同步
代码逻辑一变,假如文档示例没跟上,测试立刻失败,再也不用担心“过时文档”。 -
📦 开箱即用,零依赖
Python 标准库自带,不需要安装 pytest 或 unittest,随手就能写。 -
🤝 无缝对接文档生成器
Sphinx、MkDocs 等工具可以直接提取 docstring 里的 doctest 示例,生成漂亮的 API 文档,还能顺带自动跑测试。
🔧 基础玩法:三步上手
1. 在函数 / 类的 docstring 里写「仿真对话」
doctest 的语法非常简单,就像把 Python 解释器里的操作搬进多行注释:
- 以
>>>开头表示测试输入; - 紧接着的下一行是预期输出(如果没有输出就空着,但要保持输入/输出的顺序和真实交互一致);
- 如果测试异常,保留 traceback 的首尾行,中间部分用
...省略。
① 简单函数测试
下面是一个自带文档和测试的绝对值函数:
② 带exception-handling的类测试
再来一个可以「像访问属性一样访问字典值」的小工具:
提示:
...会帮我们省略 traceback 中间的无关细节,只要首尾信息匹配,测试就算通过。
2. 两种方式运行 doctest
方式一:在模块内部调用(适合快速自测)
在 Python 文件的最底部加上这两行代码:
然后直接运行这个文件:
不加 verbose 参数的话,遵循「无输出即通过」的 Unix 哲学——测试全部通过就安安静静,有错误才会报出来。
方式二:命令行直接调用(适合批量 / 临时测试)
不修改任何代码,一条命令搞定:
还可以直接传入一段带 >>> 的文本字符串,或者配合管道使用,非常灵活。
🚀 进阶技巧:处理“不听话”的输出
写 doctest 时,总会遇到一些输出“不太老实”的情况,比如:
- 内存地址每次运行都不一样;
- 打印时间随系统变动;
- 随机数没法预测。
别硬刚,doctest 准备了几个贴心的标记指令。
1. 动态/不可预测的输出?用 ELLIPSIS
在语句后面加上 # doctest: +ELLIPSIS,然后用 ... 代替不确定的部分,doctest 就会“通融”地只匹配你写出的内容,忽略那些变化的部分:
上面例子中,只要输出以 3 开头,后面跟了零个或多个字符,测试就通过——无论是 3.8、3.11 还是 3.12。
2. 完全不需要测试的输出?用 SKIP
有些示例纯粹是给读者看的,比如随机数展示,根本没法固定预期输出。直接加 # doctest: +SKIP 跳过验证:
3. 多行输出要怎么对齐?
doctest 匹配多行输出时,只关心每一行开头的空格和正文内容,所以只要你把真实输出“原样”抄进去就行,不用纠结绝对位置。例如 print() 打出多段文字的场景:
🎯 综合实战:一个全覆盖的阶乘函数
下面我们结合常规用例、边界值、异常测试,写一个完整、健壮的阶乘函数。这个例子几乎涵盖了前面讲到的所有知识点:
小练习:试着把上面代码保存为
factorial.py,运行一下,看看 doctest 的输出长什么样。
💡 最佳实践
-
✅ 保持测试小而精
每个>>>块只测一个功能点,清晰易维护,不用挤在一句话里测所有场景。 -
⚠️ 一定要测边界值
最小值、最大值、空输入、特殊字符……这些往往是 bug 的藏身之处。 -
❌ 避免有副作用的测试
不要在 doctest 里改全局变量、读写磁盘、连接数据库。这类操作会让测试结果不稳定,甚至污染环境。 -
🤝 和单元测试框架搭档使用
doctest 擅长“文档示例验证”,不适合复杂业务逻辑、性能测试等场景。该用 pytest / unittest 的地方就果断用。 -
📝 写对用户真正有用的示例
不要只测1 + 1,要展示用户实际会遇到的典型用法,文档的价值才能最大化。
📚 对接主流文档生成工具
doctest 的另一大优势在于,它可以和 Sphinx、MkDocs Material 等工具无缝集成,让“写文档”和“跑测试”变成一件事。
Sphinx 配置示例
在 Sphinx 项目的 conf.py 中启用相关扩展:
然后在项目根目录运行:
这一条命令不仅会测试所有 Python 模块里的 docstring,连 .rst 文档里手写的 >>> 示例也会一并验证,真正做到“文档即测试”。
🎬 总结
Python 的 doctest 是一个「性价比极高」的轻量级工具:
- 语法简单,不需要额外学习;
- 同时解决“文档示例同步”和“快速自测”两个痛点;
- 还能和专业文档工具联动,让文档真正“活”起来。
下次写完一个函数,别急着关文件,花两分钟在 docstring 里补几行 >>> 示例吧。
让你写出来的例子,再也不会坑自己,更不会坑用户!

