所谓的测试,是一种产品质量保证的手段。我遵循需求规格说明书制造了一件产品,那么谁来确保这个产品符合了需求规格的要求呢?是测试。它会根据需求规格说明书设计一系列的场景和用例,来对产品进行测试,看看产品是不是真的符合所期望的需求。
  要抵达这个主旨,其实并不十分的随意,因为一个真正的系统,状态十分复杂,里面充满了数不清的分支、异常、边界条件,甚至运行环境,将这些东西组合起来,发作的需要测试的点将会是一个天文数字,在有限的时间内做完一个短缺而可靠的测试,是不能够的。
  为了将短缺测试变得能够,一个对照好的途径是分层测试。我在做运行测试或性能测试的时候,有一个前提,是如果整个系统的集成运行已经没有问题了,在运行测试或性能测试时,我将不再推敲“系统无法正常运行”这种场景。那么如何保证集成运行没问题呢?我们用集成测试来考试。但是在做集成测试的时候,我们同样要基于一个如果,是各个模块的功能都能够如期正常义务。而这一点,又是通过模块自身的功能测试来完成的。……这样一层层往下推,每个层次如果它所依赖的层次没有问题,这样能够增加很多场景以及由这些场景引出的额外的分支。将原先一个几何级数的测试用例分解成能够吸收的若干层次的算术级数的用例。这样一来测试变得有能够做好了。
  而单元测试,正是这些测试的低层次——保证每个函数÷方法,或者说小功能模块的正确性的一种测试。
  通过上面的描述,我们至少清晰了这样几件事情:1。单元测试是一种测试,它不是代码的一部分;2。单元测试是低层级的测试,它只保证函数的可靠性,不保证其它;3。单元测试应该能保证每一个函数的可靠性。
  单元测试是一种测试,所以,我们应该以一种测试的眼光去面对它——我们要测试正常状态,边界条件,要对它的测试主旨——函数做黑盒分析,白盒分析,选择相宜的测试数据,构建测试场景和测试环境——总之,一切测试应该做的事情,单元测试都不应该省略。
  实际上来说,单元测试和其他测试一样,也是能够纯手工完成的:我们能够写一段某函数的测试代码,然后输入我们的测试输入,观察测试输入,并跟期望值做对照——事实上这种人工测试,写了一段时间代码的人应该都不会陌生。但是,单元测试有一点特殊性,是在一集系统中,函数会十分十分的多,变化也比软件的功能频繁的多。面对这么多的函数,这么频繁的变化,纯手工测试是不现实的。所以,我们必需要引入单元测试框架进行自动化测试。注意,这里的单元测试框架只是完成自动化测试的一个手段,对单元测试自身并不发作任何影响——没有单元测试框架,单元测试一样也是能够进行的,只是会痛苦很多。
  单元测试框架引入的主旨只是为了自动化单元测试,简化单元测试的步骤。所以,关于测试代码的编写,我们的重点应该是:1、如何搭建测试环境、测试场景;2、如何选择测试用例;3、如何校验测试效果。关于测试代码自身,应该尽能够的简单,能不要应用技术尽量不要应用,我们的主旨在于测试,如果测试自身过于复杂,我们不能保证测试的正确性,测试这个义务白做了。
  另外,刚刚提到单元测试是对函数的测试,因此,测试必须是以函数为单位的。每个函数应该拥有自己单独的一个测试,但是在这个测试中,我们应该针对这个函数的各个方面:正常的、异常的、边界的……等等,各个方面进行圆满的测试,这样我们才能保证这个函数的功能是如我们所愿的。但是单元测试不需要负责函数的组合义务状态。那应该是(低层次)功能测试的义务,而不是单元测试的义务。这个功能测试是在如果一切函数都义务正常的基础之上,对这些函数组合造成的功能模块进行测试。这种测试,视状态而定,能够应用单元测试框架,也能够应用其他自动化测试方法或者甚至是应用纯人工测试。
  另外,我还想讨论一下单元测试的编写和运行。
  绝大部分时候,单元测试的编写,是由开发人员做的。我们在以前某次对单元测试的讨论中,甚至有人认为,单元测试必须由开发人员完成,而不应该由独立的测试人员完成。关于这个问题,我是这样看的:测试是一种针对需求的验证义务。如果这个需求十分清晰,清晰到开发人员之外的人都能够随意掌握(有些日本外包发出来的函数说明书能抵达这一点),这时单元测试能够由独立的测试人员完成。但是大部分状态下关于函数级别,做不到这一点。这时清晰函数需求的人是开发人员自己,在这种状态下当然应该是开发人员自己编写测试用例。但是开发人员必须搞清晰自己身兼两个不同的角色:运发起(完成代码)和裁判员(考试代码),在编写测试用例的时候绝不能如果任何函数的完成,而应该完全遵循它应该有的需求来做。这样才能做好单元测试这件事。很多时候单元测试形同虚设,是因为开发人员没有很好的转换自己的角色造成的。
  单元测试的运行,目前我们这个Python的项目对照随意,直接运行模块是该模块的单元测试,而以模块形式import是实际应用。关于像C或者其他的一些语言来说,能够没有这样方便的形式。我们能够把测试写在独立的文件中,然后用makefile组合不同的项目和主函数来做到这一点。另外还有一点是,实际运行过程中能够会有一些环境,这些环境在测试时难以获得,或者增加上去之后,难以测试(比喻网络环境、数据库环境等等),这时我们能够采用一些虚拟的环境来做到。我们把运行时需要的环境做一个简化的虚拟版本,然后以这个版本作为测试环境进行测试,关于Python来说,我们能够完成这样的一个库在测试时import进来并且同时做一些环境初始化义务,在C里,我们能够专门为测试写一些运行库,在实际运行编译和测试编译时,链接不同的库。这在自动化测试技术中有个专门的名称叫做Mock Object。关于这个,我不再深入了。