图3是用两种结构测试方法构造的测试用例情况。如前所述,方法A比方法B构造出的测试用例集合更大。较大的测试用例集合就一定比小些的好吗?这是个好问题。结构测试方法可以很好地回答这个问题。通过仔细观察可以看出对这两种方法来说,所构造的测试用例集合全部包含于实现行为集合内。由于结构测试方法本身就是基于程序的,所以它不能发现未实现的行为。然而,结构测试用例集合一定会小于实现行为的全集。在第11章中,我们将比较由几种结构测试方法构造的测试用例集合。
图3 结构测试用例构造方法的比较
3、功能测试与结构测试之争
对于这两种构造测试用例集合的基本方法来说,我们自然要问哪一种更好呢?如果你研读了大量相关文献,就会发现每种方法都有许多“追随者”。Robert Poston曾对结构测试方法提出质疑:“从20世纪70年代以来,此类测试工具就是在浪费测试人员的时间……它根本就不能有效支持软件测试活动,就不应该保留在软件测试人员的工具箱中。”(Poston, 1991)但也有许多学者支持结构测试方法,代表人物Edward Miller在书中写道:“对分支覆盖率(一种测试覆盖指标)来说,如果能保证85%或更高的水平,找出的故障就能够达到'直觉'(或功能)测试方法的两倍。”(Miller, 1991)
前面给出的维恩图能够有效地解决这场争论。回想一下:这两种方法的根本出发点都是要构造测试用例。功能测试方法只利用规格说明来构造测试用例,而结构测试方法则把程序源代码(具体实现)作为构造的依据。前面的论述得出这样的结论:单独使用任何一种方法都是不够全面的。从程序行为来看:即使所有的规定行为都没有实现,结构测试也发现不了这个问题。反过来也是如此,如果程序实现了未规定行为,功能测试过程也发现不了。(特洛伊木马病毒就是此种未规定行为的例证。)结论是这两种测试方法都必不可少。经验丰富的测试专家会很明智地把两种方法结合起来,既能够获得通过功能测试法所提供的可信度,也能获得结构测试法所提供的明确的覆盖指标。前面已提过功能测试法常常受到冗余与测试不足的双重困扰,现在将在功能测试过程中引入结构测试方法的测试覆盖指标,那么这两个问题就都迎刃而解了(如图4所示)。
图4 测试用例的来源
利用维恩图可以进一步深入研究这个问题。测试用例集合T、规定行为集合S以及实现行为集合P之间是什么关系呢?显然,测试用例集合T是由采用的测试用例构造方法所决定的。在此,一个重要问题是这个方法在多大程度上是适当的(或有效的)?再回顾一下前面的讨论,审视一下从错误到故障、到失效、再到事故的因果链。如果能够了解易犯的错误都是什么,也知道在待测程序中容易出现什么样的故障,那么就完全可以利用这些信息来选用更恰当的测试用例构造方法。这就是测试之所以成为一种技艺的关键所在。