曾经看过一篇介绍某静态测试工具的文章,说该工具可以发现所有的代码错误,我觉得很好奇,问:如果我写了一个加法函数,把加号写成减号,如int add(int a, int b){return a-b;};,可以找出来吗?答:这是代码功能错误,当然不能找出来。愣了半天,没想明白:难道代码的功能错误不是代码错误?怪偶语文没学好,理解不了这种高深的文字游戏。
静态测试方法到底可以发现哪些错误?极限是什么?这要从数据和代码的自然属性和业务属性说起。
数据具有自然属性,如一个无符号32位整数,具有最大、最小值,正负数交界处如0、1、-1比较特殊,不能被零除等。这些自然属性,无论在什么代码中都是一样的。程序中的数据通常还具有业务属性,如一个无符号32位整数,在这个代码中可能表示一个人的年龄,有效值为0-200,在另一个代码中可能表示星期几,有效值为0-6。程序要对业务属性做出合适处理,如年龄小于1的为婴儿,要特殊照顾;或者星期一至五要工作,周末要休息。数据的自然属性很少,基本类型就那么几种,高级类型都是基本数型的组合。数据的业务属性则是千变万化无穷无尽的。数据的自然属性是死的,在什么代码中都一样;数据的业务属性是活的,要根据需求来确定。
代码也具有自然属性,如要符合语法规范、数组不能越界、条件语句中含有“=”则可能是“==” 的误写等等。代码更具有业务属性,也就是要实现需要的业务功能,否则写得像花一样也没有意义。相对于业务属性,代码的自然属性也是很少的,并且同一语言的代码属性是基本不变的,不管代码用来做什么,自然属性都差不多。代码的业务属性则是千变万化无穷无尽的,并且要符合设计需求。
代码的错误有可能来自自然属性,也可能来自业务属性。由于数据和代码的自然属性是简单且有限的,而业务属性则是复杂且无穷的,因此,来自业务属性错误远远高于来自自然属性的错误。
静态测试方法通过分析代码来发现错误,所依据的只能是数据和代码的自然属性,对业务属性则一无所知。这就是这类方法的极限,也就是说,静态测试方法做到极致,也只能发现一小部分错误。另外,静态分析只能基于现有代码,也不能发现代码缺失造成的错误。我看过一些静态测试的示例,基本上都是使用排序算法之类的小程序,这是一个巧妙的选择,因为这类程序中,数据只有自然属性,例如大小之分,没有业务属性。可惜的是,除非你正在编写通用底层库,否则多数代码和数据都是具有业务属性的。
要发现多数错误必须用动态测试方法,并且测试数据要人工设计,因为,只有人才能理解代码的功能,才能既了解代码和数据的自然属性,也了解业务属性。
静态方法和动态方法所发现的错误并不是互补关系,而是包含关系。人工设计用例时,既会考虑数据和代码的自然属性,也会考虑其业务属性,因此,动态方法既能发现来自自然属性的错误,也能发现来自业务属性的错误。实际上,自然属性引起的错误也会以功能错误的方式表现出来,例如,除零错误、数据越界会引起崩溃,当然也是功能错误;if(a==0)写成if(a=0)也不能实现正确功能,并且会造成else分支不可覆盖。
静态方法通常是自动的,但只是扫描过程自动,其输出通常存在大量的漏报和误报。漏报我们不说它,如果能直接报告一两个明确的错误,那也不错,毕竟是自动的,但误报则是大问题,大量的误报导致需要大量的后续人工分析,并可能掩盖真实的错误,从这一点来说,并不自动。
最方便高效的静态分析工具是编译器。百度说谁最懂中文?当然是中国人最懂中文。那么谁最懂代码?编译器最懂代码。编译警告就是很好的静态分析信息,例如,VC6的编译警告有四级,默认为三级,如果设为四级,你会发现这个十多年前的老工具,其报告潜在错误的能力不逊于一些现代的专门静态测试工具。
静态测试方法当然有它的价值,但并没有那么神奇。如果你要评估静态测试的效果,那么要考虑其他条件,例如做不做动态测试、编译器的报错功能是否已发挥?如果不做动态测试,那么静态分析并不能发现大多数错误,如果要做,静态测试则基本没有意义。我说的也许是错的,最好自己评估一下:如果是在要做动态测试的前提下评估,那么要先做动态测试,再评估静态测试;如果是在不做动态测试的前提下评估,那么,把编译器的警告级别提至最高,先解决警告问题,再评估静态测试,只有这样,才能真正判断测试测试的价值。
文章来源于领测软件测试网 https://www.ltesting.net/