认识您的验证工程师
验证工程师是测试知识的很好来源。他们可以给您指出要测试什么并帮助您了解可以在代码中加入什么以帮助他们测试(如代码钩子)。此外,您可以向他们展示如何使用您的脚手架代码。他们还会很有兴趣了解您认为在测试中哪些应该是自动化的 —— 如果您某些事情做了不止一遍,那么他们也会。
开始测验!
现在是进行小测试的时候了。让我们看看您是否用心了。
问题
您希望检查一个整数的值是否为 5。通常,要这样编写代码:
if ( i == 5 ) then
{
//
// do something...
//
}
不过,如果您对代码进行“手指检查”,并且把代码写成了下面这样会出现什么情况呢?
if ( i = 5 ) then
{
//
// do something...
//
}
这个失误是一个缺陷,但是只有在运行时才能捕获它 —— 可能需要相当的调试努力才能找到它。编译器会轻易放过您的代码,那么如何防止这种错误发生?
答案
实际上有两个答案:您可以使用一种上面描述的静态代码分析工具,并希望它有足够的健壮性可以捕获这种错误,也可以交换操作数以使常量位于左边:
if ( 5 == i ) then
{
//
// do something...
//
}
因为这种方法保证您可以在编译代码时立即捕捉到问题,所以它是首选的技术。虽然它看上去有些笨,但是代码可以编译并运行得很好。然而,当您“手指检查”代码时就可以立即看到好处了:
if ( 5 = i ) then
{
//
// do something...
//
}
可是编译器不喜欢这样,因为 5 不能被赋值为另一个值。这就是我们在前面 说您应当将编译器看成是您的朋友时所表达的意思。
您还可以在检查 null 指针时使用这个技巧。看下面的代码:
if ( returnString == null )
{
//
// do something...
//
}
如果您偶然将它“误写”成下面这样会发生什么呢?
if ( returnString = null )
{
//
// do something...
//
}
您可能不会得到想要的结果。而改用下面的方法您会得到与我们刚描述过的同样的“编译器保护”:
if ( null == returnString )
{
//
// do something...
//
}
结束语
您可能预计会有一个长的小结,把我们所讨论的所有内容重新概述一遍,然后是请您尽可能多地采用我们的建议。冒着让您失望的风险,为保持简明扼要我们做了一个相当简洁的归纳:要么现在去做,要么以后花 多得多 的代价去做。换句话说,您在开发周期的早期在测试和预防代码缺陷上花的时间越多,您在以后节省的时间和金钱就越多。这就是防御性编码的意义。它就是这么简单。