测试——unittest和doctest
单元测试当然是基本的。如果没有用于展示某个功能的单元测试,那么这个功能就不是真的存在。换句话说,对于一个功能来说,直到有测试可以说明它已经完成才算是完成。
我们只会对测试进行少量介绍。如果对每个面向对象设计功能的测试都进行深入介绍,那么这本书的厚度应该是现在的两倍。在忽略测试内容的细节上会存在一个误区,好的单元测试似乎只是可选的。当然不是,它们是必需的。
| 单元测试是必需的 如果有疑问,可以先设计测试用例,再对代码进行修改,满足测试用例。 |
Python提供了两种内置的测试框架。大部分应用和库会同时使用两者。对于所有测试来说,普遍的一种封装为unittest模块。另外,在许多公共API docstrings中可以找到一些例子,都使用了doctest模块。而且,在unittest中可以包含doctest中的一些模块。
好一点的做法是,每个类和函数都至少有一个单元测试。更重要的是,可见的类、函数和模块也要包含doctest。还有更好的做法:100%的代码覆盖,100%的逻辑分支覆盖等。
实际上,一些类不需要测试。例如由namedtuple()创建的类不需要单元测试,除非首先去怀疑namedtuple()的实现。如果不相信Python的实现,就无法基于它来写程序。
一般地,我们会先设计测试用例再编写可以通过测试用例的代码。测试用例会凸显出代码中API的形态。本书会介绍几种写代码的方式,它们的接口是相同的,这点很重要。一旦我们定义了接口,会有几种不同的实现方式。一组测试应该能够适应几种不同面向对象的设计。
一种常见的方式是使用unittest工具为项目创建至少有以下3种平行的目录。
- myproject:这个目录会需要安装在lib/site-packages中,作为应用最终的包。它会有一个init.py包,而且会放在每个模块中。 -test:这个目录包含测试脚本。对于一些情形,这些脚本在模块中是平行存在的。在一些情况下,脚本可能会很大并且比模块自身更复杂。
- doc:这个目录中会包含其他文档。我们会在下一节以及第18章“质量和文档”中对它进行介绍。
在一些情况下,你会希望在多个类上运行同样的测试组件,这样就能够确保每个类是工作的。但在根本不工作的类上使用timeit进行比较是没有意义的。
