All About Smart Testing

测试杂感:代码覆盖率的目的

上一篇 / 下一篇  2010-05-09 13:33:44 / 个人分类:杂感

上一篇杂感提到同事用Windbg提高代码覆盖率,我也做过相似的事情。也是临近项目结束,也是项目的发布标准对代码覆盖率有定量要求,不同的是这次的测试对象是.NET应用。这个项目应用了LINQ to SQL技术,代码中有许多ORM类。这些类是代码生成工具的产出,一个类对应数据库中的一张表,其属性(Property)对应表的字段。开发人员图方便,生成了所有表对应的类,其所有属性都支持读写(get/set)。不幸的是,这个应用只访问部分表,而且大多是只读访问。这导致这些ORM类的代码覆盖率非常低。

当时,我可以选择修改代码覆盖率报告,手工地排除掉这些类。但是,我也是程序员,要用程序员的方式来解决这个问题。受到Windbg提高代码覆盖率的启发,我用IronPython Console加载(load)被测试程序集(assembly),实例化ORM类的对象,然后访问其属性。2分钟后,我发现这些类具备相同的模式:拥有无参数的构造函数,除了属性外没有其他成员,所以属性都是基本类型(数值、字符串、日期)。于是,我编写了一个几十行的IronPython程序来自动访问这些类和它们的属性。其核心代码如下:

1:  def access_attribute(namespace):
2:      for name in dir(namespace):
3:          try:
4:              type_ = getattr(namespace, name)
5:              if type(type_) == type(System):
6:                  access_attribute(type_)
7:                  continue
8:
9:                  bj = type_()
10:                 for attr in dir(obj):
11:                     try: setattr(obj, attr, type(getattr(obj, attr))())
12:                     except: pass
13:                     try: setattr(obj, attr, None)
14:                     except: ass
15:                     try: setattr(obj, attr, '')
16:                     except: pass
17:          except:
18:              pass

该函数递归遍历名字空间,尝试用无参数构造函数实例化对象,然后访问对象属性。

  • 第2行:获取namespace中的元素的名称,并遍历。
  • 第4行:获取namespace中的元素,并存放在type_中。在.NET平台上,type_只可能是类或名字空间。
  • 第5~7行:如果该元素的类型与System的类型相同,则说明该元素也是名字空间,那么递归遍历该名字空间。
  • 第9行:尝试用无参数构造函数实例化type_的对象,并存放在obj中。
  • 第10~16行:遍历obj对象的成员,并尝试对其赋值。
  • 代码中出现了大量的try语句块,这保证出现异常时,整个遍历过程不会被中断。

这个Python代码大约花了我15分钟。然后,我利用它加载程序集,从根名空间开始遍历。结果是喜人的:几分钟后,所有的ORM代码被覆盖,一些具备无参数构造函数的类也被不同程度的覆盖。代码覆盖率在绝对值上有10%的增长(或者说,有大约10%的代码是无用的)!我倍受鼓舞,决定用两个步骤进一步完善这个程序。

  1. 用Python内省(introspection)或.NET反射来获取函数的形参列表。根据形参列表,构造实参列表,并调用函数。这样就可以实例化大部分非抽象类,并调用其公有函数。
  2. 利用演化测试(evolutionary testing)来自动提高代码覆盖率。在大学的时候,我研究过演化测试,并用C++实现了一个演化测试的原型,为论文提供实验数据。现在,演化测试已经渐渐从实验室走向工业应用。微软研究院的Pex利用符号执行、演化测试等技术,可以自动生成全语句覆盖的单元测试

开发一个测试平台的想法确实很有吸引力,但是我不得不面对现实。15分钟提高10%确实很经济,投入几天的时间再提高10%值得么?捕获所有的异常,只管调用,不管结果,这是测试吗?获取代码覆盖率的目的是什么?设置代码覆盖率标准的目的又是什么?

正如测试是对代码的反馈,代码覆盖率就是对测试的反馈。获取代码覆盖率,不是要证明测试的充分性,而是要发现测试的遗漏,从而补充测试用例,以发现隐藏的缺陷。设置代码覆盖率标准,是提醒开发团队努力提高测试的覆盖率,以降低未发现缺陷的数目。所以,代码覆盖率是一种寻找缺陷的技术。它是提高测试的手段,不是测试的目的。只收集代码覆盖率,却不利用它去发现更多的缺陷,是误入歧途。

在我的工作中,我总是发现我或他人错误地理解了代码覆盖率的目标,并采取了高投入、“零产出”的方法来提高代码覆盖率。这篇博文就反映了技术人员的思维模式:将所有问题都转化为技术问题,然后提供技术性的解决方案,却忘却了最初的目的。

当然,我不是在暗示Pex是无用的。你研究一下,就会发现Pex将技术放在一个合理的位置。它通过与程序员合作来提供高质量的解决方案:程序员提供业务逻辑验证(以前后置条件的形式),Pex利用智能算法和日趋强大的CPU来提供精悍的测试输入。所以,紧盯目标,充分发挥各种资源的优势,才能事半功倍。


TAG:

引用 删除 xiaowo   /   2010-06-19 10:46:35
5
FISHY'S TRIBE 引用 删除 fishy   /   2010-05-11 13:31:08
您好,我是51Testing软件测试网的编辑,您的本篇博文被推荐至51Testing软件测试网首页发表:http://www.51testing.com/html/16/n-213716.html
感谢您关注并支持51Testing博客,期待您更多的优秀原创博文。
Visual Unit 官方博客 引用 删除 VisualUnit   /   2010-05-10 18:18:33
统计覆盖率的目的是为了找出遗漏用例,这不是很简单的问题吗?
引用 删除 墨卓   /   2010-05-10 18:03:59
学习来了!
引用 删除 墨卓   /   2010-05-10 18:03:34
5
 

评分:0

我来说两句

Open Toolbar