集成测试是测试和组装软件的系统化技术,例如,子系统测试即是在把模块按照设计要求组装起来的同时进行测试,主要目标是发现与接口有关的问题(系统测试与此类似)。例如,数据穿过接口时可能丢失;一个模块对另一个模块可能由于疏忽而造成有害影响;把子功能组合起来可能不产生预期的主功能;个别看来是可以接受的误差可能积累到不能接受的程度;全程数据结构可能有问题等等。不幸的是,可能发生的接口问题多得不胜枚举。
由模块组装成程序时有两种方法:一种方法是先分别测试每个模块,再把所有模块按设计要求放在一起结合成所要的程序,这种方法称为非渐增式测试方法;另一种方法是把下一个要测试的模块同已经测试好的那些模块结合起来进行测试,测试完以后再把下一个应该测试的模块结合进来测试。这种每次增加一个模块的方法称为渐增式测试,这种方法实际上同时完成单元测试和集成测试。这两种方法哪种更好一些呢?下面对比它们的主要优缺点。
非渐增式测试一下子把所有模块放在一起,并把庞大的程序作为一个整体来测试,测试者面对的情况十分复杂。测试时会遇到许许多多的错误,改正错误更是极端困难,因为在庞大的程序中想要诊断定位一个错误是非常困难的。而且一旦改正一个错误之后,马上又会遇到新的错误,这个过程将继续下去,看起来好像永远也没有尽头。
当使用渐增方式把模块结合到程序中去时,有自项向下和自底向上两种集成策略。
自顶向下集成方法是一个日益为人们广泛采用的测试和组装软件的途径。从主控制模块开始,沿着程序的控制层次向下移动,逐渐把各个模块结合起来。在把附属于(及最终附属于)主控制模块的那些模块组装到程序结构中去时,或者使用深度优先的策略,或者使用宽度优先的策略。
参看图6.3深度优先的结合方法先组装在软件结构的一条主控制通路上的所有模块。选择一条主控制通路取决于应用的特点,并且有很大任意性。例如,选取左通路,首先结合模块M1、M2和M5;其次,M8或M6(如果为了使M2具有适当功能需要M6)将被结合进来。然后构造中央的和右侧的控制通路。而宽度优先的结合方法是沿软件结构水平地移动,把处于同一个控制层次上的所有模块组装起来。对于图6.3来说,首先结合模块M2、M3、和M4(代替存根程序S4),然后结合下一个控制层次中的模块M5、M6和M7;如此继续进行下去,直到所有模块都被结合进来为止。
图6.3 自顶向下结合
把模块结合进软件结构的具体过程由下述4个步骤完成。
第一步,对主控制模块进行测试,测试时用存根程序代替所有直接附属于主控制模块的模块。
第二步,根据选定的结合策略(深度优先或宽度优先),每次用一个实际模块代换一个存根程序(新结合进来的模块往往又需要新的存根程序)。
第三步,在结合进一个模块的同时进行测试。
第四步,为了保证加入模块没有引进新的错误,可能需要进行回归测试(即,全部或部分地重复以前做过的测试)。
从第二步开始不断地重复进行上述过程,直到构造起完整的软件结构为止。图6.3描绘了这个过程。假设选取深度优先的结合策略,软件结构已经部分地构造起来了,下一步存根程序S7将被模块M7取代。M7可能本身又需要存根程序,以后这些存根程序也将被相应的模块所取代。
自顶向下的结合策略能够在测试的早期对主要的控制或关键的抉择进行检验。在一个分解得好的软件结构中,关键的抉择位于层次系统的较上层,因此首先碰到。如果主要控制确实有问题,早期认识到这类问题是很有好处的,可以发早想办法解决。如果选择深度优先的结合方法,可以在早期实现软件的一个完整的功能并且验证这个功能。早期证实软件的一个完整的功能,可以增强开发人员和用户双方的信心。
自顶向下的方法讲起来比较简单,但是实际使用时可能遇到逻辑上的问题。这类问题中最常见的是,为了充分地测试软件系统的较高层次,需要在较低层次上的处理。然而在自顶向下测试的初期,存根程序代替了低层次的模块,因此,在软件结构中没有重要的数据自下往上流。为了解决这个问题,测试人员有两种选择:第一,把许多测试推迟到用真实模块代替了存根程序以后再进行;第二,从层次系统的底部向上组装软件。第一种方法失去了在特定的测试和组装特定的模块之间的精确对应关系,这可能导致在确定错误的位置和原因时发生困难。后一种方法称为自底向上的测试,下面讨论这种方法。
自底向上测试从“原子”模块(即在软件结构最低层的模块)开始组装和测试。因为是从底部向上结合模块,总能得到所需的下层模块处理功能,所以不需要存根程序。
用下述步骤可以实现自底向上的结合策略。
第一步,把低层模块组合成实现某个特定的软件子功能的族。
第二步,写一个驱动程序(用于测试的控制程序),协调测试数据的输入和输出。
第三步,对由模块组成的子功能族进行测试。
第四步,去掉驱动程序,沿软件结构自下向上移动,把子功能族组合起来形成更大的子功能族。
上述第二步到第四步实质上构成了一个循环图。描6.4描绘了自底向上的结合过程。首先把模块组合成族1、族2和族3,使用驱动程序(图中用虚线方框表示)对每个子功能族进行测试。族1和族2中的模块附属于模块Ma,去掉驱动程序D1和D2,把这两个族直接同Ma连接起来。类似地,在和模块Mb结合之前去掉族3的驱动程序.最终Ma和Mb这两个模块都与模块Mc结合起来。
图6.4 自底向上结合
随着结合向上移动,对测试驱动程序的需要也减少了。事实上,如果软件结构的顶部两层用自顶向下的方法组装,可以明显减少驱动程序的数目,而且族的结合也将大大简化。
上面介绍了集成测试的两种策略,到底哪种方法更好一些呢?一般说来,一种方法的优点正好对应于另一种方法的缺点。自顶向下测试方法的主要优点是不需要测试驱动程序,能够在测试阶段的早期实现并验证系统的主要功能,而且能在早期发现上层模块的接口错误。自顶向下测试方法的主要缺点是需要存根程序,可能遇到与此相联系的测试困难,低层关键模块中的错误发现较晚,而且用这种方法在早期不能充分展开人力。可以看出,自底向上测试方法的优缺点与上述自顶向下测试方法的优缺点刚好相反。
在测试实际的软件系统时,应该根据软件的特点以及工程进度安排,选用适当的测试策略。一般说来,纯悴自顶向下或纯粹自底向上的策略可能都不实用,人们在实践中创造出许多混合策略。
(1)改进的自顶向下测试方法。基本上使用自顶向下的测试方法,但是在早期使用自底向上的方法测试软件中的少数关键模块。一般的自顶向下方法所具有的优点在这种方法中也都有,而且能在测试的早期发现关键模块中的错误;但是,它的缺点也比自项向下方法多一条,即测试关键模块时需要驱动程序。
(2)混合法。对软件结构中较上层使用的自顶向下方法与对软件结构中较下层使用的自底向上方法相结合。这种方法兼有两种方法的优点和缺点,当被测试的软件中关键模块比较多时,这种混合法可能是最好的折衷方法。
在集成测试过程中每当一个新模块结合进来时,程序就发生了变化:建立了新的数据流路径,可能出现了新的I/O操作,激活了新的控制逻辑。这些变化有可能使原来工作正常的功能出现问题。在集成测试的范畴中,所谓回归测试是指重新执行已经做过的测试的某个子集,以保证上述这些变化没有带来非预期的副作用。
更广义地说,任何成功的测试都会发现错误,而且错误必须被改正。每当改正软件错误的时候,软件配置的某些成分(程序、文档或数据)也被修改了。回归测试就是用于保证由于调试或其他原因引起的变化,不会导致非预期的软件行为或额外错误的测试活动。
回归测试可以通过重新执行全部测试用例的一个子集人工地进行,也可以使用自动化的捕获回放工具自动进行。利用捕获回放工且,软件工程师能够捕获测试用例和实际运行结果,然后可以回放(即重新执行测试用例),并且比较软件变化前后所得到的运行结果。
回归测试集(已执行过的测试用例的子集)包括下述3类不同的测试用例。
(1)检测软件全部功能的代表性测试
(2)专门针对可能受修改影响的软件功能的附加测试。
(3)针对被修改过的软件成分的测试。
在集成测试过程中,回归测试用例的数量可能变得非常大。因此,应该把回归测试集设计成只包括可以检测程序每个主要功能中的一类或多类错误的那样一些测试用例。一旦修改了软件之后就承新执行检测程序每个功能的全部测试用例,是低效而且不切实际的。