开发单元测试
开发者是如何知道应创建的测试类型以及何时测试呢?这个问题没有明确的答案。我们要知道的是开发过程中单元测试是最经常被用到的,另外它的目的是在开发初期尽可能地减少bug,从而减少测试量并确保在其在高级测试中的正确性。将软件分割成单元来测试也迫使开发者创建更优化的软件构架,这能有助于查找bug和软件维护。
测试法可分为行为测试和结构化测试。行为测试确保程序的行为符合设计的意图。例如,当网络数据流不适用目标缓冲器时该方法使其成为可能。结构化测试在另一方面确保所有重要的控制路径的传输都被测试过。
实际上,首先创建行为测试其次度量科覆盖是非常有用的。然后在结构分析的基础上添加测试实例直到实现必要的覆盖。
这章我们将详细介绍普遍的测试技术。
● 黑盒测试对白盒测试
黑盒测试是一种通过在测试环境下不需要任何内部知识知识来检查对象的方式。只需检查输入和输出。
这种测试法的不足之处是我们很难确保所有的路径都被测试到。尽管如此,对于那些大型的复杂系统,依然更倾向于使用黑盒测试简化事情的方法。
另一方面白盒测试的目的是要运用尽可能多的内部相关技术知识为对象进行测试。它允许测试者选择能使所有(或最重要的部分)路径都能被测试的测试输入方式。路径可由控制构架和获取数据来构建。代码覆盖工具自动运行来寻找路径并报告它们的测试的进展如何。详细内容可查看Wikipedia [6]。
自从白盒测试反映对象的内部工作组以来,当测试发生改动时,它就需要升级。而黑盒测试只需在方法签名和语义发生变化时才被修改。
● 行为测试技术
一种非常普遍的代码错误是对边界问题进行不适当的操作。边界值分析的观点是指在边界区域进行测试。例如,MIN, MIN-1,MAX, MAX+1。这些值在(当使用)输入和输出函数时都应被检验。
我们不可能创建所有可能的系统会用到的函数输入数据,尽管如此,相似的数据被函数使用时并不改变其执行路径,所以这些数据可被抽象化。等价类分割就是被设计用来把所有的相似输入值划分成类的行为测试。例如,如果函数接受-5到15的值,我们就只需要三个测试部分:
* 有效: [-5 – 15]
* 无效: [-n – -6]
* 无效: [16 – n]
特殊值是错误产生的一个重要原因,它们需要函数额外注意,比如以下例子:
* 0和1的算术运算和函数使用
* 90度和多重复合
* 空字符串
* NULL值
经验和直觉能指引开发者预测代码错误的可能性。因为程序经常产生相似错误,通过错误猜想能使测试用例的创建更加实际有效。特别是当软件和开发组有进展时这是个很实际的方法,而且测试套件随之逐渐运行。如果有改动,这样的测试能标记出软件代码中充斥的bug。
相关错误猜想包括:
* 不适当或缺失的错误操作,例如,用RFile.Open()代替User::LeaveIfError(RFile.Open()).
* 不适当的异常操作,例如,捕获异常而忽略问题(当产品中存在异常时,它将使软件死掉)。(堆或其他资源)代码泄露,正确使用 cleanup stack 是一种为自动变量及类变量的构造和析构提供的很好的方法。当在指针后面删除对象时,这些指针在进行任何(可能产生退出的)操作时都应被置NULL直到在析构函数里被删除。
* 不恰当的语义转换。例如,在函数里被分片写在文件里的数据首先是调用而不是集合所有所需数据,然后将其写在一个原子操作里。如果操作中发生异常则第一种方法很容易破坏该文件。
* 不恰当地使用CleanUpStack和CActive。
* 无效的事件处理,例如,当事件按不同顺序发生时会有什么结果?通常的办法是设计一个有限状态机,然后简单地实现这个状态机。
* 对象的生命周期由其所处的状态不同而存在差异,例如,当参考对象不期望时(如果没有正确考虑或引证,调用返回值以及产生这种状况的活动对象)参对象被删除。
* 多线程, 并行执行和死锁。