单元测试(防御性测试技术)
在本文中,我们所说的 单元测试 是开发人员在自己的代码正确编译后、在交给功能测试小组之前进行的所有测试和分析。正如我们在这只是一个测试 中提到的,主动进行单元测试并 在测试时像一位测试者那样思考(即,必须往坏处想、热衷于破坏并喜欢恶作剧)是很重要的。下面是在单元测试时要记住的几件事。
静态代码分析工具
第一种,也是最容易的分析代码的方法是让别人替您做 —— 或者像在这里一样,让其他 工具 替您做。有一些不同的静态代码分析工具可用,从综合性的工具 —— 一些开发机构实际上在他们的“编译”环境(这可是需要购买的)中加入了这样的工具 —— 到其他可以免费从 Internet 上下载的工具。有关可用的静态代码分析工具的信息请参阅 参考资料。
发现缺陷
当您准备运行代码并检查缺陷时,要记住往坏处想。这些缺陷是您所创建的或者由您忽略的代码产生。下面是一些帮助您找到代码中缺陷的提示:
试着强行制造您所想到的所有错误条件并检查可以出现的所有错误消息。
试着使用与其他组件或者程序交互的代码路径。如果其他程序或者组件还不存在,那么就自己编写一些脚手架代码以使您可以试用 API 或者填充共享内存、共享队列,等等。并让您的功能测试小组可以使用这个脚手架代码,这样他们就可以把它加入到他们的武器库中。
对于 GUI 中的每一个输入字段,试验下面多种不同的组合(考虑 自动化):
不可接受的字符(控制字符、非打印字符等)。
过多的字符。
过少的字符。
负数(特别是当您只期待正数时)。
过大和/或者过小的数。
剪切和粘贴数据和文本到输入字段,特别是当您编写的代码限制用户可以键入该字段的内容时。
文本和数字的组合。
全大写字母和全小写字母。
为代码创建“压力条件”,如大量活动、慢连接的网络和所有您想到的可以将代码推到极限条件的东西。
反复进行同样的步骤,然后:
检查未预计到的内存损失条件。
检查当内存用光时发生什么。
试图创建缓存溢出、满队列、不可用的缓存以及其他“不能正确工作”的情况。
对于数组和缓存,试着向数组(或者缓存)增加 n 个数据项,然后试图删除 n+1 个项。
关于时间的考虑?
如果操作“b”在操作“a”之前发生会怎么样?就算您 认为 它不会发生 —— 您能 使 它发生吗?如果是的话,可以打赌您的客户会使它发生的。最好现在找出来,而不是在修复成本更高、并听到客户报怨您的软件质量糟糕之后再去做。
脚手架代码
我们在前面发现缺陷 中讨论了脚手架代码。如果是为自己的使用需要而创建的,一定要将它交给验证工程师。可能您提供的脚手架代码使他们可以很快地开始测试您的代码,或者至少使他们更好地理解当其他组件存在时可以预期什么。
如果您的产品有保护性的安全功能,那么您必须测试它们。提供可以创建您想要防止的情况的脚手架代码是很重要的:您必须能够创建系统试图防止的那种情况。
脚手架代码的另一个简单例子是提供操纵队列的代码。如果您的产品使用队列,那么想像如果有一个可以在运行时从队列中增加或者删除项、破坏队列中的数据(以保证适当的错误处理)等等的工具会有多方便。
源代码级调试程序
使用源代码级的调试程序是进行彻底和成功的单元测试的关键方法。开发人员应该与他们的调试程序共生死。不幸的是,对源代码级的调试程序的充分了解和使用是一种正在消亡的做法,尽管这些调试程序的好处远远超过任何学习曲线。简而言之,我们强烈鼓励您全面学习一种调试程序。下面是用源代码级调试程序对代码进行单元测试的几种方法。您可以:
在运行中操纵数据 —— 例如,在输入代码时设置中断点,然后重新设置传递的参数值以检查代码是否能正确处理(现在为)无效的参数。以这种方式使用调试程序就不需要让错误条件真正发生。
设置断点,然后“单步”通过代码,这样您就可以看到每一行代码所做的事情。
设置对变量的“监视(watch)”。
强制使用错误代码路径。
观察调用堆栈以检查哪一个例程调用了您的代码。
在错误发生时“捕获”它们。
执行边界检查。