Python模块的单元测试基础设施

8 投票
6 回答
1050 浏览
提问于 2025-04-16 01:16

我正在写一个Python模块,想要对它进行单元测试。我对Python还不太熟悉,对可用的选项有点困惑。

目前,我想把我的测试写成doctests,因为我喜欢声明式的风格,而不是命令式的风格(不过,如果我这个想法不对,请随时纠正我)。不过,这也引出了几个问题:

  1. 我应该把测试放在哪里?是放在和代码在同一个文件里(或者放在doctests的文档字符串里)?还是说把它们分开放到一个单独的目录里更好?
  2. 我怎么能从命令行一次性运行整个模块里的所有测试?
  3. 我怎么能报告测试套件的代码覆盖率?
  4. 还有其他关于Python单元测试的最佳实践我应该知道的吗?

6 个回答

2

我有一种感觉,Alex在编程方面可能比我更有经验。不过,如果你想听听一个有一些Python经验的人的看法(我只是个使用者,不是专家或推广者),我对单元测试的发现和他的差不多。

一开始,使用doctests听起来很不错,适合简单的测试。我在家里做个人项目时也尝试过,因为在别的地方有人推荐过。工作中我们用的是nose(虽然它被包装得很复杂,我之前还以为我们用的是pyUnit),几个月前我在家也开始用nose了。

一开始设置和管理这些测试可能会让人觉得麻烦,尤其是当你的代码量不大的时候,似乎没必要把测试和实际代码分开。但从长远来看,我发现doctests在我想重构或调整代码时总是会给我带来麻烦,维护起来很困难,几乎不可能扩展,而且最初的时间节省很快就被这些问题抵消了。没错,我知道单元测试和集成测试不一样,但doctests往往对你的测试单元定义得太严格了。如果你决定把它当作一种有效的草图工具或开发模型,它也不太适合基于单元的敏捷开发。

虽然一开始可能需要花点时间来规划和完善你的单元测试,按照pyUnit或nose的建议来做,但你会发现,即使在短期内,它们在很多方面都能帮到你。我知道对我来说确实是这样,而我现在正在处理的代码库的复杂性和规模对我来说还是比较新的。只要咬紧牙关,前几周就能熬过去。

3

我不喜欢 doctests,原因有以下几点:

  • 你不能只运行一部分测试。当某个测试失败时,能单独运行一个测试是很有用的。但 doctest 没有这个功能。
  • 如果在 doctest 运行过程中出现错误,整个测试就会停止。我更希望能看到所有的测试结果,这样可以更好地判断如何解决问题。
  • 代码的风格比较特殊,结果必须是可打印的。
  • 你的代码是以一种特殊的方式执行的,这让你更难理解它是如何运行的,也更难添加辅助功能,甚至更难围绕测试进行编程。

这份清单来自我在博客上的一篇文章 我不喜欢 doctest 的一些事情,里面还有更多内容,以及一长串评论讨论这些观点。

关于覆盖率:我认为没有工具可以测量 Python 中 doctests 的覆盖率。但因为它们只是长长的语句列表,没有分支或循环,这算不算问题呢?

12

如果我这个想法有误,请随意纠正我。

我觉得我在我的项目中使用doctest的频率比其他开源开发者都要高,甚至有点超出了它的本来用途——我在我的gmpy项目中的所有测试都是用doctest写的。那时候doctest刚刚出现,我觉得这是个很不错的小技巧,既然做事情值得,那就应该尽量做到最好,对吧?

错了。除了gmpy,因为如果把所有东西都重新做成标准的单元测试会太麻烦,我再也没有犯过这个错误:现在我用单元测试来做单元测试,而用doctest只是检查我的文档,正如它本来应该被使用的那样。doctest的功能(就是比较预期结果和实际结果是否相等——就这么简单)并不是建立一个稳固测试套件的好基础。它从来就不是这个意思。

我建议你看看nose。新的Python 2.7中的unittest模块功能更丰富、更好用,如果你还在用2.4、2.5或2.6版本,你仍然可以通过下载和安装unittest2来使用新功能;noseunittest搭配得很好。

如果你对unittest感到厌烦(不过——试试看,它会让你慢慢喜欢上!),也可以试试py.test,这是一个理念完全不同的替代包。

但是,不要把doctest用来测试文档以外的东西!精确比较结果的方式会让你遇到很多麻烦,正如我在gmpy中所学到的那样……

撰写回答